The Apple logo is a registered trademark of Apple Computer, Inc. Use of the “keyboard”
Apple logo (Option-Shift-K) for commercial purposes without the prior written consent of
Apple may constitute trademark infringement and unfair compe-tition in violation of federal and state laws.
Apple Computer, Inc.
20525 Mariani Avenue
Cupertino, CA 95014-6299
408-996-1010
Apple, the Apple logo, APDA, AppleLink, AppleShare,
AppleTalk, Apple IIGS, A/UX, EtherTalk, HyperCard, Hyper-Talk, ImageWriter, LaserWriter, LocalTalk, Mac, Macintosh, MPW, MultiFinder, SANE, and TokenTalk are registered trade-marks of Apple Computer, Inc.
Apple Desktop Bus, Balloon Help, Finder, KanjiTalk, Moof, QuickDraw, ResEdit, TrueType, and Zhong-Wen Talk are trade-marks of Apple Computer, Inc.
Helvetica and Times are
registered trademarks of
Linotype Company.
ITC Zapf Dingbats is a registered trademark of International Typeface Corporation.
MacPaint is a registered trade-mark of Claris Corporation.
NuBus is a trademark of Texas
Instruments.
PostScript is a registered trade-mark, and Illustrator is a
trademark, of Adobe Systems
Incorporated.
Sony is a registered trademark of Sony Corporation.
UNIX is a registered trademark of UNIX System Laboratories, Inc.
Simultaneously published in the United States and Canada.
Limited Warranty on Media and Replacement
Even though Apple has
reviewed this manual, APPLE MAKES NO WARRANTY OR REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS MANUAL, ITS QUALITY, ACCURACY, MERCHANT-ABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. AS A RESULT, THIS
MANUAL IS SOLD “AS
IS,” AND YOU, THE PUR-CHASER, ARE ASSUMING THE ENTIRE RISK AS TO ITS QUALITY AND
ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL,
INCIDENTAL, OR CONSE-QUENTIAL DAMAGES
RESULTING FROM ANY
DEFECT OR INACCURACY IN THIS MANUAL, even if advised of the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE ARE EXCLUSIVE AND IN LIEU OF ALL
OTHERS, ORAL OR
WRITTEN, EXPRESS OR
IMPLIED. No Apple dealer, agent, or employee is
authorized to make any
modification, extension, or
addition to this warranty.
Some states do not allow the exclusion or limitation of
implied warranties or liability for incidental or consequential damages, so the above
limitation or exclusion may not apply to you. This warranty gives you specific legal rights, and you may also have other rights which vary from state to state.
The Device Manager
Introduction to Devices and Drivers1-3
About the Device Manager1-4
Using the Device Manager1-7
Opening and Closing Device Drivers1-8
Communicating With Device Drivers1-10
Controlling and Monitoring Device Drivers1-13
Reference to the Device Manager1-15
Data Structures1-15
Routines1-18
Opening and Closing Device Drivers1-18
Reading From and Writing To Devices1-25
Controlling and Monitoring Devices1-31
Summary of the Device Manager1-39
1The Device Manager
This chapter describes how your application can use the Device Manager to transfer information into and out of the Macintosh. The Device Manager controls the exchange of information between applications and hardware devices. Often applications communicate with the Device Manager indirectly, by calling routines of other managers (for example, the File Manager) which use the Device Manager. However, sometimes applications must call Device Manager routines directly.
Read the information in this chapter if your application needs to use the Device Manager to communicate with a device. For example, you might need to use the Device Manager to communicate with a modem device on an expansion card. You would probably not need to use the Device Manager to communicate with a graphics device on an expansion card, as you would use QuickDraw instead. You also do not need to use the Device Manager directly to communicate with disk drives or printers, as you can use the File Manager or the Printing Manager.
To use this chapter, you should be familiar with resources and how the system searches resource files. See the chapter “The Resource Manager” in the Inside Macintosh: Macintosh Toolbox volume.
This chapter includes
n a brief introduction to devices and device drivers (the programs that control devices)
n a description of the Device Manager and how it manages device drivers
n a discussion of how to use Device Manager routines to exchange information with devices and device drivers
You can find specific information about the standard Macintosh device drivers, like the Disk Driver and the Serial Drivers, in separate chapters of this book. If you want to create your own device driver or desk accessory, you should first read this chapter and then read the chapter “Writing Your Own Device Driver” in this book.
Introduction to Devices and Drivers
A device is a physical part of the Macintosh, or a piece of external equipment, that can exchange information with applications or with the Operating System. Input devices transfer information into the Macintosh, while output devices receive information from the Macintosh. An I/O device can transfer information in either direction.
Devices transfer information in one of two ways. Character devices read or write a stream of characters, or bytes, one at a time; they can neither skip bytes nor go back to previous bytes. The serial ports and printers are character devices.
Block devices read and write entire blocks of bytes at one time; they can read or write any accessible block on demand. These devices are usually used to store and retrieve information. Disk drives are block devices.
Devices communicate with applications and with the Operating System through special programs called device drivers. These drivers act as translators, converting software requests into hardware actions and hardware actions into software results. A synchronous device driver completes each communication request before returning control to the system. An asynchronous device driver can initiate a data transfer and return control to the system before the entire transfer is complete. This type of device driver usually relies on interrupts from its hardware device to regain control of the processor and continue or complete the data transfer.
A device driver does not have to be associated with a device. In general, a device driver is a program that conforms to a predefined format and programming interface. Many device drivers take advantage of this predefined interface to perform tasks unrelated to actual physical devices. Desk accessories are an example of this kind of device driver.
Some device drivers are stored in the Macintosh ROM; others are stored in driver ('DRVR') resources, which are typically located in applications, system extension files, and the firmware of expansion cards. A driver resource contains information about the driver, including the driver name, and the code necessary for the driver to perform its functions. The Device Manager must load a driver from its driver resource into memory before your application or any part of the system can communicate with it.
The Device Manager opens certain device drivers at system startup time. These drivers make up the standard device drivers, which include the Disk Driver, the Serial Drivers, and printer drivers. The Device Manager opens other drivers when requested—typically by initialization ('INIT') resources, applications, and other drivers.
About the Device Manager
The Device Manager provides three basic services:
n It provides a common programming interface for applications and other managers to use when communicating with device drivers.
n It maintains data structures to manage open device drivers.
n It provides support routines useful when writing your own device drivers.
Typically, your application won’t communicate directly with device drivers; instead, it will call Device Manager routines or call the routines of another manager that calls the Device Manager. For example, your application can communicate with a disk driver by calling the Device Manager directly or by calling the File Manager, which calls the Device Manager.
Figure 1-1 shows the relationship between applications, the Device Manager, other managers, device drivers, and devices.
Communication with Devices
Before the Device Manager allows an application or another manager to communicate with a device driver, the driver must be open, which means the Device Manager has received a request to open the driver, has loaded the driver into memory if necessary, and has successfully called the driver’s opening routine.
When opening a device driver, the Device Manager creates a device control entry (DCE), which contains information about the device driver. For example, the device control entry contains a handle to the device driver code (or a pointer for device drivers in ROM). Typically, the Device Manager maintains one device control entry for each open device driver, but it is possible for multiple entries to refer to the same driver.
The Device Manager maintains another data structure, the unit table, to organize the device control entries. The unit table contains a handle to the device control entry for each open driver. The location of a driver’s device control entry in the unit table is called the driver’s unit number.
When you open a device driver, the Device Manager returns a driver reference number for the driver. You use the driver reference number, instead of the driver name, to identify the driver in subsequent communication requests. The driver reference number is related to the unit number by the following formula:
The Device Manager also maintains a driver request queue for each open device driver. At the head of this queue is the I/O request currently being processed by the device driver. The rest of the queue contains pending I/O requests—requests the Device Manager has received but not yet sent to the device driver. This queue allows your application to request a data transfer with a busy device and accomplish other tasks while the device satisfies previous requests.
With respect to the driver request queue, the Device Manager allows you make to three types of requests: synchronous, asynchronous, and immediate.
The terms synchronous and asynchronous have different meanings depending on their context.
When referring to a device driver, the term synchronous indicates that the driver completes the entire request before returning control to the Device Manager. The term asynchronous indicates that the driver can begin processing a request and return control to the Device Manager before the request is complete. When a driver is handling a request asynchronously, it usually relies on interrupts from the hardware device to continue processing the request.
When referring to requests, the terms synchronous and asynchronous indicate how the Device Manager processes the request. Actually, you can specify that the Device Manager process the request in one of three ways.
n Synchronous requests. When you make a synchronous request, the Device Manager places your request at the end of the driver request queue. The Device Manager then waits until the driver has handled every request in the queue, including the synchronous one, before returning control to your application. Notice there can never be more than one synchronous request per driver request queue at any given time. (The driver can handle each request in the queue synchronously or asynchronously, but the Device Manager does not return control to your application until the final request is complete.)
n Asynchronous requests. When you make an asynchronous request, the Device Manager places your request at the end of the driver request queue, but it returns control to your application immediately—potentially before the request is satisfied. Your application is free to perform other tasks while the driver satisfies the requests in its queue. The Device Manager provides mechanisms for your application to determine when the driver has satisfied the request.
n Immediate requests. When you call a Device Manager routine immediately, the Device Manager sends your request directly to the driver, bypassing the driver request queue, and returns control to your application when the request is complete. An asynchronous device driver, which can process a single communication request in a number of steps, might be in the middle of processing another request. A similar situation can exist even for a synchronous device driver if you make the immediate request during interrupt time. Device drivers that can handle this situation correctly are called reentrant drivers. As some device drivers are not reentrant, you should always check a driver’s documentation before making immediate requests.
The next section, “Using the Device Manager,” describes how to specify the manner in which the Device Manager should process your request.
The chapter “Writing Your Own Device Driver” in this book gives more information about the Device Manager data structures and also describes the Device Manager routines useful for writing device drivers.
Using the Device Manager
Your application can use Device Manager routines to communicate with devices. It can use these routines to open and close device drivers, to exchange information with them, and to otherwise control and monitor them.
The Device Manager includes high-level and low-level versions of most of its routines. The high-level versions are somewhat easier to use, but they allow less control of how the Device Manager executes the routine (for example, they always execute synchronously) and they return less information to your application. Conversely, the low-level routines require some additional setup, but they allow you greater control and return more information.
The high-level routines differ in form, but the low-level routines all have the form:
FUNCTION PBRoutineName (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
The first parameter paramBlock is a Device Manager parameter block. You can use fields of this data structure to pass more complete information to the driver than you can with high-level routines, and the driver uses the same data structure to pass information back. The section “Data Structures,” on page 1-14, discusses the fields of the parameter block in detail.
The second parameter async specifies whether the Device Manager should execute the routine asynchronously. If you set this parameter to FALSE, the Device Manager adds the parameter block to the driver request queue and waits until the driver completes the request (which means it has completed all previously queued requests) before returning control to your application.
If you set the async parameter to TRUE, the Device Manager adds the parameter block to the driver request queue and returns control to your application immediately. In this case, a noErr result code signifies that the request was successfully queued, not that the request was successfully completed. Your application can determine when the driver has completed the request by periodically polling the ioResult field of the parameter block. The Device Manager sets this field to 1 when the request is queued, and the driver stores the actual result code there when it completes the request.
You can also provide a pointer to a completion routine in the ioCompletion field of the parameter block. The Device Manager executes this routine when the driver completes the asynchronous request.
ASSEMBLY-LANGUAGE NOTE
You can call a Device Manager routine asynchronously, bypassing the request queue, by setting the immediate bit (bit 9) of the trap word. However, remember that the driver might have an asynchronous request pending. In fact, if you make an immediate request during interrupt time, the driver might be currently executing another request. Be sure the driver is reentrant—that is, that it can handle these situations.<36pt\>\x12 <8bat\>u
Opening and Closing Device Drivers
You must open a driver before your application can communicate with it. The Device Manager provides three functions for opening device drivers: OpenDriver, PBOpen, and OpenSlot. Each of these functions requires a driver name and returns a driver reference number.
A driver name consists of a period (.) followed by any sequence of 1 to 254 printing characters; for example, .ATP is the name of one of the high-level AppleTalk drivers.
The initial period in a driver name allows the Device Manager and the File Manager, which both use the PBOpen function, to distinguish between device driver names and file names. For the same reason, filenames should not start with a period.<36pt\>\x12 <8bat\>u
The Device Manager assigns each open device driver a driver reference number. This number allows the Device Manager to locate the driver’s device control entry in the unit table.
The OpenDriver function, which is the high-level opening routine, takes the driver name as its first parameter and returns the driver reference number in its second parameter.
Listing 1-1 shows how to use the OpenDriver function to open a driver from a resource file.
Opening a device driver from a resource file
VAR gDrvrRefNum : Integer;
PROCEDURE MyOpenDriver;
VAR
drvrHdl : Handle;
drvrID : Integer;
tempDrvrID : Integer;
drvrType : ResType;
drvrName : Str255;
err : OSErr;
BEGIN
tempDrvrID := FindEmptySpaceInUnitTable;
drvrHdl := GetNamedResource('DRVR','.DRIVER');
GetResInfo(drvrHdl, drvrID, drvrType, drvrName);
SetResInfo(drvrHdl, tempDrvrID, drvrName);
err := OpenDriver('.DRIVER',gDrvrRefNum);
IF (err = NoErr) THEN
DetachResource(drvrHdl);
drvrHdl := GetNamedResource('DRVR',drvrName);
SetResInfo(drvrHdl,drvrID, drvrName);
END;
The OpenDriver function uses the resource ID of the driver resource to determine where in the unit table to store the device control entry for the driver. Unfortunately, the OpenDriver function does not check to see if another device control entry is already located in that position in the unit table.
Therefore, the MyOpenDriver procedure begins by finding an empty space in the unit table and then changes the resource ID of the driver resource before calling the OpenDriver function. (The chapter “Writing Your Own Device Driver” gives a complete definition of the FindEmptySpaceInUnitTable function.)
After the driver is open, the MyOpenDriver procedure detaches the resource and restores the original resource ID.
The PBOpen and OpenSlot functions, which are low-level functions, use the Device Manager parameter block, which is described fully in “Data Structures” on page 1-14. Both of these functions expect a pointer to the driver name in the ioNamePtr field of the parameter block and return the reference number in the ioRefNum field.
You use the PBOpen and OpenSlot functions in preference to the OpenDriver function when you want more control over how the Device Manager opens the device driver. For example, you can set read and write permissions for the device with the ioPermssn field of the parameter block.
You use the OpenSlot function to open drivers that serve slot devices. This function uses an extended parameter block that includes extra fields specific to slot device drivers. You use the PBOpen function to open any other device drivers.
You must open device drivers synchronously. If a device driver is already open, the driver-opening routines simply return the driver reference number.
The remaining Device Manager routines require your application to use the reference number, instead of the driver name, when referring to a device.
When you are done using a driver, you may want to close it. However, you should generally not close drivers that might be needed by the system or by other applications. Whether you should close a particular driver depends on the driver. You should check the documentation for the driver if you are not sure. Other chapters in Inside Macintosh give this information for the standard Macintosh drivers.
If you do want to close a driver, you can use the high-level CloseDriver function or the low-level PBClose function. Listing 1-2 shows how to use the PBCLose function to close the driver opened in Listing 1-1.
Closing a device driver
PROCEDURE MyCloseDriver;
VAR
paramBlock : ParamBlockRec;
err : OSErr;
BEGIN
paramBlock.ioCompletion := nil;
paramBlock.ioRefNum := gDrvrRefNum;
err := PBClose (@paramBlock, FALSE);
END;
The MyCloseDriver procedure specifies the driver to close by providing the driver reference number in the ioRefNum field of the parameter block and indicates that the Device Manager should not execute a completion routine by specifying NIL in the ioCompletion field of the parameter block.
Communicating With Device Drivers
Once a driver is open and you have its reference number, you can use Device Manager communication routines to exchange information with it.
When you want to send information to a device driver, you first store the information in a data buffer and then call the FSWrite function or the PBWrite function. When you want to receive information from a device driver, you first allocate a data buffer to hold the information, and then call the FSRead or PBRead function. You must specify the number of bytes you want transferred when calling any of these functions.
Like all low-level routines, the PBWrite and PBRead functions allow you to transfer the information asynchronously, and with these functions you can provide a completion routine. These functions also allow you to specify a drive number, a positioning mode, and a positioning offset in the ioVRefNum, ioPosMode, and ioPosOffset fields of the parameter block. The Device Manager uses the positioning mode and positioning offset to determine where on a block device the transfer will occur. The Device Manager simply passes the drive number onto the driver. Be sure you specify these values in a manner compatible with the particular driver.
The Device Manager defines three positioning modes for block devices:
n At the current position. With this mode, the transfer begins at the current position on the medium—typically where the last transfer ended.
n Offset from the start. This mode allows you to provide a positioning offset, which specifies at how many bytes from the beginning of the medium the transfer will begin.
n Offset from the mark. With this mode you can provide a positioning offset that specifies at how many bytes from the current position the transfer will begin.
On completion, the PBRead and PBWrite functions return the total number of bytes actually transferred in the ioActCount field of the parameter block. For block devices, these functions also return a new positioning offset in the ioPosOffset field.
Certain device drivers provide additional abilities with the reading and writing functions. For example, the Disk Driver allows you to use the PBRead function to verify that data written to a block device matches the data in memory. To do this, you add the read-verify constant
CONST rdVerify = 64;
to the ioPosMode field of the parameter block, as explained in the description of the PBRead function in “Routines,” on page 1-25.
Listing 1-3 shows an example that exchanges information with a device driver.
Communicating with a device driver
PROCEDURE MyReadFromDriver;
VAR
paramBlock : ParamBlockRec;
err : OSErr;
readBuf : Str255;
BEGIN
paramBlock.ioCompletion := nil;
paramBlock.ioRefNum := gDrvrRefNum;
paramBlock.ioBuffer := @readBuf;
paramBlock.ioReqCount := 256;
err := PBRead (@paramBlock, FALSE);
END;
PROCEDURE MyWriteToDriver;
VAR
paramBlock : ParamBlockRec;
err : OSErr;
buffer : Str255;
BEGIN
buffer := 'Data to Write';
paramBlock.ioCompletion := nil;
paramBlock.ioRefNum := gDrvrRefNum;
paramBlock.ioBuffer := @buffer;
paramBlock.ioReqCount := 14;
err := PBWrite (@paramBlock, FALSE);
END;
The MyReadFromDriver procedure uses a parameter block to specify the device driver (by reference number), the number of bytes to read, a pointer to a buffer to receive the data, and NIL for the completion routine. It then sends the parameter block to the PBRead function. The Device Manager appends the parameter block to the end of the driver’s request queue. Since the parameter async is set to FALSE, the Device Manager does not return control to the MyReadFromDriver procedure until the driver has completed every request in its queue.
The MyWriteToDriver procedure uses a parameter block to specify the device driver (by reference number), the size of the data to write, a pointer to the actual data, and NIL for the completion routine. It then sends the parameter block to the PBWrite function. The Device Manager appends the parameter block to the end of the driver’s request queue. Since the parameter async is set to FALSE, the Device Manager does not return control to the MyWriteToDriver procedure until the driver has completed every request in its queue.
Controlling and Monitoring Device Drivers
In addition to the reading and writing routines, the Device Manager provides routines that allow your application to control and monitor device drivers in other ways.
The Control and PBControl functions allow your application to send control information to a driver. The actual type of control information to which drivers respond varies greatly from driver to driver. For example, you can send control information to the standard Disk Driver requesting that it eject the disk.
The Status and PBStatus functions allow your application to obtain status information from a driver. Again, the type of information drivers provide varies widely from driver to driver. As an example of status information, the Serial Driver provides a breakdown of the types of errors that have occurred recently.
Like all low-level routines, you can call the PBControl and PBStatus functions asynchronously, and with these functions you can provide a completion routine.
Because of the diversity of device drivers, the controlling and monitoring functions have two general parameters: csCode and csParamPtr. The csCode parameter allows you to specify a driver-dependent code that indicates the type of control or status information. The csParamPtr parameter allows you to send or receive the actual control or status information.
Notice that although the controlling and monitoring functions have a different interface than the reading and writing functions, their outcome is much the same: they exchange information with device drivers. Some device drivers (AppleTalk, for instance) implement all communication, even reading and writing, through the controlling and monitoring functions.
The sample device driver described in the chapter “Writing Your Own Device Driver” accepts control and status requests. Listing 1-4 shows how to provide control information and determine status information for that device driver.
Controlling and monitoring a device driver
PROCEDURE MyIssueDriverControl;
VAR
paramBlock : ParamBlockRec;
err : OSErr;
BEGIN
paramBlock.ioCompletion := nil;
paramBlock.ioCRefNum := gDrvrRefNum;
paramBlock.csCode := 100;
paramBlock.csParam[0] := 3; { beep 3 times }
err := PBControl (@paramBlock, FALSE);
END;
PROCEDURE MyGetDriverStatus;
VAR
paramBlock : ParamBlockRec;
err : OSErr;
beepTimes : Integer;
BEGIN
paramBlock.ioCompletion := nil;
paramBlock.ioCRefNum := gDrvrRefNum;
paramBlock.csCode := 100;
err := PBStatus (@paramBlock, FALSE);
IF (err = noErr) THEN
beepTimes := paramBlock.csParam[0];
END;
The MyIssueDriverControl procedure begins by setting up the fields of a parameter block. The ioCRefNum field specifies the device driver reference number and the ioCompletion field specifies NIL for the completion routine. The csCode field specifies the type of control information being sent. The sample device driver from the chapter “Writing Your Own Device Driver” interprets a value of 100 as a request for the device driver to produce a system beep. When the value of the csCode field is 100, the value of the csParam field signifies the number of times to beep.
The MyGetDriverStatus procedure also begins by setting up the fields of a parameter block. The ioCRefNum field specifies the device driver reference number and the ioCompletion field specifies NIL for the completion routine. The csCode field specifies the type of status information being requested. For this particular device driver, the value 128 indicates the driver should return the number of times it last beeped. The PBStatus function causes the driver to store this information in the csParam field of the parameter block.
Reference to the Device Manager
This section describes the data structures and routines that allow you to use the Device Manager to communicate with device drivers.
The “Data Structures” section shows the Pascal definition for the Device Manager parameter block. The “Routines” section describes the routines you use to open and close device drivers, read from and write to device drivers, and control and monitor device drivers. Constants used by these routines are listed in the section “Summary of the Device Manager,” on page 1-37.
The chapter “Writing Your Own Device Driver” in this book describes the Device Manager data structures and routines useful when writing a device driver.
Data Structures
The Device Manager provides both a high-level and a low-level interface for communicating with device drivers. You pass information to the low-level routines in a Device Manager parameter block.
The Device Manager uses two forms of the parameter block: one for the opening, closing, reading, and writing routines and another for controlling and monitoring routines. Other managers use different variants of this data structure.
ioVRefNum : Integer; {volume reference or drive number}
CASE ParamBlkType OF
ioParam:
(ioRefNum: Integer; {driver reference number }
ioVersNum: SignedByte; {not used }
ioPermssn: SignedByte; {read/write permission }
ioMisc: Ptr; {not used}
ioBuffer: Ptr; {pointer to data buffer}
ioReqCount: LongInt; {requested number of bytes }
ioActCount: LongInt; {actual number of bytes }
ioPosMode: Integer; {positioning mode }
ioPosOffset: LongInt); {positioning offset }
cntrlParam:
(ioCRefNum: Integer; {driver reference number }
csCode: SignedByte; {type of request }
csParam: ARRAY[1..10] OF Integer);
{control or status info }
END;
END;
qLink Used internally by the Device Manager.
qType Used internally by the Device Manager.
ioTrap Used internally by the Device Manager.
ioCmdAddr Used internally by the Device Manager.
ioCompletion Specifies a pointer to a completion routine. When making asynchronous requests, you must set this field to NIL if you are not specifying a completion routine. The Device Manager automatically sets this field to NIL when you make a synchronous request.
ioResult Contains a value indicating whether the routine completed successfully. The Device Manager sets this field to 1 when it queues an asynchronous request. When the driver completes the request, it places the actual result code in this field. You can poll this field to detect when the driver has completed the request and to determine its result code. The Device Manager executes the completion routine after this field receives the result code.
ioNamePtr Points to the name of the driver. You use this field only when opening a driver.
ioVRefNum Contains the drive number, if any. The meaning of this field depends on the device driver. The Disk Driver uses this field to identify disk devices.
I/O field descriptions
ioRefNum Specifies the driver reference number.
ioVersNum Not used.
ioPermssn Contains the read/write permission of the driver. When you open a driver, you must supply one of the following values in this field:
CONST fsCurPerm = 0; {retain current permission}
fsRdPerm = 1; {allow reads only}
fsWrPerm = 2; {allow writes only}
fsRdWrPerm = 3; {allow reads and writes}
The Device Manager compares subsequent read and write requests with the read/write permission of the driver. If the request type is not permitted, the Device Manager returns a a result code indicating the error.
ioMisc Not used.
ioBuffer Points to the data buffer for the driver to use for reads or writes.
ioReqCount Specifies the requested number of bytes for the driver to read or write.
ioActCount Indicates the actual number of bytes the driver reads or writes.
ioPosMode Specifies the positioning mode used by drivers of block devices. Bits 0 and 1 of this field indicate where an operation should begin relative to the physical beginning of the block-formatted medium. You can use the following constants to test or set the value of these bits:
CONST fsAtMark = 0; {at current position}
fsFromStart = 1; {offset from beginning}
fsFromMark = 2; {offset from current pos.}
The Disk Driver allows you to add the following constant to this field to specify a read-verify operation:
CONST rdVerify = 64; {read-verify mode}
See the description of the PBRead function in “Reading From and Writing To Devices,” on page 1-25.
ioPosOffset Specifies the byte offset, relative to the position specified by the positioning mode, where the driver should perform the operation. If you specify the fsAtMark positioning mode, the Device Manager ignores this field.
Control and status field descriptions
ioCRefNum Contains the driver reference number.
csCode Contains a value identifying the type of the control or status request. Each driver may interpret this number differently.
csParam Contains the actual control or status information for the request; this field is declared generically as an array of ten integers because its actual format varies from one type of control or status request to the next.
Routines
This section describes the Device Manager routines you use to open and close device drivers, read from and write to device drivers, and control and monitor device drivers.
The low-level routines in this section accept a pointer to a parameter block as a parameter. For these routines, the routine description includes a list of the fields in the parameter block that are used by the routine.
Opening and Closing Device Drivers
A driver must be open before your application can communicate with it. You can use the OpenDriver or PBOpen function to open closed drivers or to determine the driver reference number of an already open device driver. You can use the OpenSlot function to open drivers that serve slot devices.
When you have finished communicating with a driver, you can close it if you are sure no other application or part of the system needs to use it. You can use the CloseDriver or PBClose function to close a driver.
The PBOpen and PBClose functions use the I/O fields of the parameter block. The OpenSlot function uses the I/O fields and some additional fields that apply only to slot devices.
1OpenDriver
You can use the OpenDriver function to open a closed device driver or to determine the reference number of an open device driver.
FUNCTION OpenDriver (name: Str255;
VAR refNum: Integer): OSErr;
name Specifies the name of the driver to open. A driver name consists of a period (.) followed by any sequence of 1 to 254 printing characters. The Device Manager ignores case (but not diacritical marks) when comparing names.
refNum Contains the reference number of the driver. The OpenDriver function uses this parameter to return the reference number of the open driver.
DESCRIPTION
The OpenDriver function opens the device driver specified by the name parameter and returns its reference number in the refNum parameter. The Device Manager searches the drivers that are already installed in the unit table before searching driver resources to avoid replacing a current driver. If the specified driver is already open, this function simply returns the driver’s reference number.
If the specified driver is not already open, the Device Manager searches for a driver resource with the specified name. If the Device Manager finds such a resource, the resource ID determines the location in the unit table where the Device Manager installs the driver’s DCE. Since another driver might already be installed in the unit table at the location determined by the resource ID, you should first search for an unused location in the unit table and renumber the resource accordingly before calling this function. See Listing 1-1 on page 1-8 for an example.
The OpenDriver function is a high-level version of the low-level PBOpen function. Use the PBOpen function when you need to specify read/write permission for the driver. See the description of the PBOpen function on page 1-18.
SPECIAL CONSIDERATIONS
The OpenDriver function may move memory; you should not call it at interrupt time.
RESULT CODES
noErr 0 No error
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
openErr –23 Driver can’t perform requested open
dInstErr –26 Couldn’t find driver in resource file
SEE ALSO
For information about the low-level routines for opening devices, see the next section, which describes the PBOpen function, and the description of the OpenSlot function on page 1-20.
For an example of opening a device driver, see Listing 1-1 on page 1-8.
1PBOpen
You can use the PBOpen function to open a closed device driver or to determine the reference number of an open device driver.
FUNCTION PBOpen (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is
asynchronous. You should always set this field to FALSE.
You specify values and receive return values in a Device Manager parameter block.
<symbol\>\xae ioCompletion ProcPtr A pointer to completion routine.
<symbol\>\xac ioResult OSErr The result code of the function.
<symbol\>\xae ioNamePtr StringPtr
A pointer to the driver name.
<symbol\>\xac ioRefNum Integer The driver reference number.
<symbol\>\xae ioPermssn SignedByte
Read/write permission.
DESCRIPTION
The PBOpen function opens the device driver specified by the ioNamePtr field, installing it if necessary, and returns its reference number in the ioRefNum field. The Device Manager searches the drivers that are already installed in the unit table before searching driver resources to avoid replacing a current driver. If the specified driver is already open, this function returns the driver reference number already belonging to the driver.
If the specified driver is not already open, the Device Manager searches for a driver resource with the specified name. If the Device Manager finds such a resource, the resource ID determines the location in the unit table where the Device Manager installs the driver’s DCE. Since another driver might already be installed in the unit table at the location determined by the resource ID, you should first search for an unused location in the unit table and renumber the resource accordingly before calling this function. See Listing 1-1 on page 1-8 for an example.
If the driver returns a negative result in register D0, the Device Manager returns the result code in the ioResult parameter and does not open the driver.
SPECIAL CONSIDERATIONS
The PBOpen function may move memory; you should not call it at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the PBOpen function is _Open ($A000). You must setup register A0 with the address of the parameter block. When _Open returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No error
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
openErr –23 Driver can’t perform requested open
dInstErr –26 Couldn’t find driver in resource file
SEE ALSO
For information about the high-level routine for opening device drivers, see the previous section, which describes of the OpenDriver routine.
For information about the low-level routine for opening device drivers that serve devices on expansion cards, see the next section, which describes of the OpenSlot routine.
For an example of opening a device driver, see Listing 1-1 on page 1-8.
1OpenSlot
You can use the OpenSlot function to open a device driver that serves a slot device.
FUNCTION OpenSlot (paramBlock: ParmBlkPtr;
async: Boolean) : OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is
asynchronous. You should always set this field to FALSE.
You specify values and receive return values in a Device Manager parameter block with a few additional fields that apply only to slot devices.
<symbol\>\xae ioCompletion ProcPtr Pointer to completion routine.
<symbol\>\xae ioMix Ptr Reserved for use by driver Open routine.
<symbol\>\xae ioFlags Integer Determines number of additional fields.
<symbol\>\xae ioSlot SignedByte
Slot number.
<symbol\>\xae ioId SignedByte
Slot resource ID.
Additional fields—multiple devices
<symbol\>\xae ioMix Ptr Reserved for use by driver Open routine.
<symbol\>\xae ioFlags Integer Determines number of additional fields.
<symbol\>\xae ioSEBlkPtr Ptr Pointer to external parameter block.
DESCRIPTION
The OpenSlot function is equivalent to the PBOpen function, except that it sets the immediate bit (bit 9) of the trap word, which signals the _Open routine that the parameter block includes additional fields.
If the slot resource, which is defined in the chapter “The Slot Manager” in this book, serves a single device, you should clear all the bits of the ioFlags field and include the slot number and slot resource ID in the ioSlot and ioID fields.
If the slot resource serves multiple devices, you should set the fMulti bit of the ioFlags field (clearing all other bits to 0), and specify, in the ioSEBlkPtr field, an external parameter block that is customized for the devices installed in the slot.
See the chapter “The Slot Manager” in this book for more details.
SPECIAL CONSIDERATIONS
The OpenSlot function may move memory; you should not call it at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the OpenSlot function is _Open ($A200). The immediate bit (bit 9) of the trap word is set to signal to that the parameter block contains the additional fields.
You must setup register A0 with the address of the parameter block. When _Open returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No error
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
openErr –23 Driver can’t perform requested open
dInstErr –26 Couldn’t find driver in resource file
SEE ALSO
For information about the low-level routine for opening other device drivers, see the previous section, which describes the PBOpen routine.
For an example of opening a device driver, see Listing 1-1 on page 1-8.
1CloseDriver
You can use the CloseDriver function to close an open device driver.
FUNCTION CloseDriver (refNum: Integer) : OSErr;
refNum Contains the reference number of the driver returned by the driver-opening function.
DESCRIPTION
The CloseDriver function closes the device driver indicated by the refNum parameter. The Device Manager completes any pending I/O for this driver and releases the memory used by the driver.
You should not close drivers that other applications may be using, such as the Disk Driver, the AppleTalk drivers, and so on.<36pt\>\x12 <8bat\>s
The CloseDriver function is a high-level version of the low-level PBClose function. Use the PBClose function when you want to specify a completion routine. See the description of the PBClose function on page 1-22.
SPECIAL CONSIDERATIONS
The CloseDriver function does not move memory; you may call it at interrupt time.
RESULT CODES
noErr 0 No Error
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Close request aborted by KillIO
SEE ALSO
For information about the low-level routine for closing device drivers, see the next section, which describes the PBClose function.
1PBClose
You can use the PBClose function to close an open device driver.
FUNCTION PBClose (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is asynchronous.
You specify values and receive return values in a Device Manager parameter block.
<symbol\>\xae ioCompletion ProcPtr Pointer to completion routine.
The PBClose function closes the device driver specified by the ioRefNum field. The Device Manager completes any pending I/O for this driver and releases the memory used by the driver.
You should not close drivers that other applications may be using, such as the Disk Driver, the AppleTalk drivers, and so on.<36pt\>\x12 <8bat\>s
If the driver returns a negative result in register D0, the Device Manager returns this result code in the ioResult parameter and does not close the driver.
SPECIAL CONSIDERATIONS
The PBClose function does not move memory; you may call it at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the PBClose function is _Close ($A001). Set bit 10 of the trap word to execute this routine asynchronously. Set bit 9 to execute it immediately.
You must setup register A0 with the address of the parameter block. When _Close returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Close request aborted by KillIO
SEE ALSO
For information about the high-level routine for closing device drivers, see the previous section, which describes the CloseDriver function.
Reading From and Writing To Devices
You can use either the FSRead or PBRead function to read information from a device driver and you can use the FSWrite or PBWrite function to write information to a device driver.
The PBRead and PBWrite functions use the additional I/O fields of the parameter block, as described on page 1-15.
1FSRead
You can use the FSRead function to read data from an open driver into a data buffer.
FUNCTION FSRead (refNum: Integer; VAR count: LongInt;
buffPtr: Ptr): OSErr;
refNum Specifies the reference number of the driver.
count Indicates the number of bytes to read.
buffPtr Points to a data buffer to hold the data.
DESCRIPTION
Before calling the FSRead function, your application should allocate a data buffer large enough to hold the data to be read. The FSRead function attempts to read the number of bytes indicated by the count parameter from the specified device driver, and transfer them to this data buffer. After the transfer is complete, the count parameter indicates the number of bytes actually read.
Be sure your buffer is large enough to hold the number of bytes specified by the count parameter, or this function may corrupt memory. <36pt\>\x12 <8bat\>s
The FSRead function is a high-level version of the low-level PBRead function. Use the PBRead function when you want to request asynchronous reading or need to specify a drive number or a positioning mode and offset. See the next section, which describes the PBRead function.
SPECIAL CONSIDERATIONS
The FSRead function does not move memory. However, you should be sure the device driver does not move memory in response to a read request before you call this function at interrupt time.
RESULT CODES
noErr 0 No Error
readErr –19 Driver can’t respond to read requests
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Read aborted by KillIO
SEE ALSO
For information about the low-level routine for reading from device drivers, see the next section, which describes the PBRead function.
1PBRead
You can use the PBRead function to read data from an open driver into a data buffer.
FUNCTION PBRead (paramBlock: ParmBlkPtr; async: Boolean) : OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is asynchronous.
You specify values and receive return values in a Device Manager parameter block.
<symbol\>\xae ioCompletion ProcPtr Pointer to completion routine.
Before calling the PBRead function, your application should allocate a data buffer large enough to hold the data to be read. The PBRead function attempts to read the number of bytes indicated by the ioReqCount field from the specified device driver and transfer them to this data buffer. After the transfer is complete, the ioActCount field indicates the number of bytes actually read.
Be sure your buffer is large enough to hold the number of bytes specified by the count parameter, or this function may corrupt memory. <36pt\>\x12 <8bat\>s
For block devices such as disk drivers, the PBRead function allows you to specify a drive number in the ioVRefNum field and specify a positioning mode and offset in the ioPosMode and ioPosOffset fields. Bits 0 and 1 of the ioPosMode field indicate where an operation should begin relative to the physical beginning of the block-formatted medium. You can use the following constants to test or set the value of these bits:
CONST fsAtMark = 0; {at current position}
fsFromStart= 1; {offset from beginning}
fsFromMark = 2; {offset from current position}
After the transfer is complete, the ioPosOffset field indicates the current position of the block device.
The Disk Driver allows you to use the PBRead function to verify that data written to a block device matches the data in memory. To do this, call PBRead immediately after writing the data, and add the read-verify constant
CONST rdVerify = 64; {read-verify mode}
to the ioPosMode field of the parameter block.
SPECIAL CONSIDERATIONS
The PBRead function does not move memory. However, you should be sure the device driver does not move memory in response to a read request before you call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the PBRead function is _Read ($A002). Set bit 10 of the trap word to execute this routine asynchronously. Set bit 9 to execute it immediately.
You must setup register A0 with the address of the parameter block. When _Read returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No error
readErr –19 Driver can’t respond to read requests
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Read aborted by KillIO
ioErr –36 Data doesn’t match in read-verify mode
SEE ALSO
For information about the high-level routine for reading from device drivers, see the previous section, which describes the FSRead function.
1FSWrite
You can use the FSWrite function to write data from a data buffer to an open driver.
FUNCTION FSWrite (refNum: Integer; VAR count: LongInt;
buffPtr: Ptr) : OSErr;
refNum Specifies the reference number of the driver.
count Indicates the number of bytes to write.
buffPtr Points to the data buffer that holds the data.
DESCRIPTION
The FSWrite function attempts to write the number of bytes indicated by the count parameter from the data buffer pointed to by the buffPtr parameter to the specified device driver. After the transfer is complete, the count parameter indicates the number of bytes actually written.
The FSWrite function is a high-level version of the low-level PBWrite function. Use the PBWrite function when you want to request asynchronous writing or need to specify a drive number or a positioning mode and offset. See the next section, which describes the PBWrite function, for further information.
SPECIAL CONSIDERATIONS
The FSWrite function does not move memory. However, you should be sure the device driver does not move memory in response to a write request before you call this function at interrupt time.
RESULT CODES
noErr 0 No error
writErr –20 Driver can’t respond to write requests
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Write aborted by KillIO
SEE ALSO
For information about the low-level routine for writing to device drivers, see the next section, which describes the PBWrite function.
1PBWrite
You can use the PBWrite function to write data from a data buffer to an open driver.
FUNCTION PBWrite (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is asynchronous.
You specify values and receive return values in a Device Manager parameter block.
<symbol\>\xae ioCompletion ProcPtr Pointer to completion routine.
The PBWrite function attempts to write the number of bytes indicated by the ioReqCount field from the data buffer pointed to by the ioBuffer field to the driver specified by the ioRefNum field. After the transfer is complete, the ioActCount field indicates the number of bytes actually written.
For block devices such as disk drivers, the PBWrite function allows you to specify a drive number in the ioVRefNum field and specify a positioning mode and offset in the ioPosMode and ioPosOffset fields. Bits 0 and 1 of the ioPosOffset field indicate where an operation should begin relative to the physical beginning of the block-formatted medium. You can use the following constants to test or set the value of these bits:
CONST fsAtMark = 0; {at current position}
fsFromStart= 1; {offset from beginning}
fsFromMark = 2; {offset from current position}
After the transfer is complete, the ioPosOffset field indicates the new current position of a block device.
SPECIAL CONSIDERATIONS
The PBWrite function does not move memory. However, you should be sure the device driver does not move memory in response to a write request before you call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the PBWrite function is _Write ($A003). Set bit 10 of the trap word to execute this routine asynchronously. Set bit 9 to execute it immediately.
You must setup register A0 with the address of the parameter block. When _Write returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No error
writErr –20 Driver can’t respond to read requests
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Write aborted by KillIO
SEE ALSO
For information about the high-level routine for writing to device drivers, see the previous section, which describes the FSWrite function.
Controlling and Monitoring Devices
You can use either the Control or PBControl function to send control information to a device driver, and you can use the Status or PBStatus function to obtain status information to a device driver. The Device Manager provides the KillIO and PBKillIO functions for terminating all requests in a driver’s request queue.
The PBControl, PBStatus, and PBKillIO functions use the additional control and status fields of the parameter block, as described on page 1-16.
1Control
You can use the Control function to send control information to a device driver.
FUNCTION Control (refNum: Integer; csCode: Integer;
csParamPtr: Ptr): OSErr;
refNum Contains the reference number identifying the driver.
csCode Contains a driver-dependent code specifying the type of information sent.
csParamPtr Contains a pointer to the actual control information.
DESCRIPTION
The Control function sends information to the device driver specified by the reference number in the refNum parameter. The value you pass in the csCode parameter and the type of information pointed to by the csParamPtr parameter depend on the driver being called. For more information, see the specific chapters for the standard drivers.
The Control function is a high-level version of the low-level PBControl function. Use the PBControl function, described in the next section, if you need to specify a drive number or if you want the control request to be asynchronous.
SPECIAL CONSIDERATIONS
The Control function does not move memory. However, you should be sure the device driver does not move memory in response to a control request before you call this function at interrupt time.
RESULT CODES
noErr 0 No error
controlErr –17 Driver can’t respond to this control request
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Control request aborted by KillIO
SEE ALSO
For information about the low-level routine for controlling device drivers, see the next section, which describes the PBControl function.
1PBControl
You can use the PBControl function to send control information to a device driver.
FUNCTION PBControl (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is asynchronous.
You specify values and receive return values in a Device Manager parameter block.
<symbol\>\xae ioCompletion ProcPtr Pointer to completion routine.
The PBControl function sends information to the device driver specified by the ioCRefNum field. The value you pass in the csCode field and the type of information in the csParamPtr parameter depend on the driver being called. For more information, see the specific chapters for the standard drivers.
SPECIAL CONSIDERATIONS
The PBControl function does not move memory. However, you should be sure the device driver does not move memory in response to a control request before you call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the PBControl function is _Control ($A004). Set bit 10 of the trap word to execute this routine asynchronously. Set bit 9 to execute it immediately.
You must setup register A0 with the address of the parameter block. When _Control returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No error
controlErr –17 Driver can’t respond to this control request
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Control request aborted by KillIO
SEE ALSO
For information about the high-level routine for controlling device drivers, see the previous section, which describes the Control function.
1Status
You can use the Status function to obtain status information from a device driver.
FUNCTION Status (refNum: Integer; csCode: Integer;
csParamPtr: Ptr): OSErr;
refNum Contains the reference number identifying the driver.
csCode Contains a driver-dependent code specifying the type of information requested.
csParamPtr Contains a pointer to the status information received.
DESCRIPTION
The Status function returns information about the device driver having the reference number specified by the refNum parameter. The value you pass in the csCode parameter, and the received information pointed to by the csParamPtr parameter depend on the driver being called. For more information, see the specific chapters for the standard drivers.
The Status function is a high-level version of the low-level PBStatus function. Use the PBStatus function if you need to specify a drive number or if you want the status request to be asynchronous.
SPECIAL CONSIDERATIONS
The Status function does not move memory. However, you should be sure the device driver does not move memory in response to a status request before you call this function at interrupt time.
RESULT CODES
noErr 0 No error
statusErr –18 Driver can’t respond to this status request
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Status request aborted by KillIO
SEE ALSO
For information about the low-level routine for monitoring device drivers, see the next section, which describes the PBStatus function.
1PBStatus
You can use the PBStatus function to obtain status information from a device driver.
FUNCTION PBStatus (paramBlock: ParmBlkPtr;
async: Boolan): OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is asynchronous.
You specify values and receive return values in a Device Manager parameter block.
<symbol\>\xae ioCompletion ProcPtr Pointer to completion routine.
The PBStatus function returns information about the device driver having the reference number specified by the ioCRefNum field. The value you pass in the csCode field and the information received in the csParam field depend on the driver being called. For more information, see the specific chapters for the standard drivers.
SPECIAL CONSIDERATIONS
The PBStatus function does not move memory. However, you should be sure the device driver does not move memory in response to a status request before you call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the PBStatus function is _Status ($A005). Set bit 10 of the trap word to execute this routine asynchronously. Set bit 9 to execute it immediately.
You must setup register A0 with the address of the parameter block. When _Status returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No error
statusErr –18 Driver can’t respond to this status request
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
abortErr –27 Status request aborted by KillIO
SEE ALSO
For information about the high-level routine for monitoring device drivers, see the previous section, which describes the Status function.
1KillIO
You can use the KillIO function to terminate all current and pending I/O with a device driver.
FUNCTION KillIO (refNum: Integer): OSErr;
refNum Contains the reference number identifying the driver.
DESCRIPTION
The KillIO function stops any current I/O request being processed by the driver indicated by the ioRefNum field and removes all pending I/O requests from the request queue for that driver. The Device Manager calls the completion routine for each pending I/O request with the ioResult field of each request equal to the result code abortErr.
The Device Manager passes KillIO requests to a driver only if the driver is open and enabled for control calls.
The KillIO function terminates all pending I/O for a driver, including I/O initiated by other applications.<36pt\>\x12 <8bat\>s
SPECIAL CONSIDERATIONS
The KillIO function does not move memory. However, you should be sure the device driver does not move memory in response to this request before you call this function at interrupt time.
RESULT CODES
noErr 0 No error
statusErr –18 Driver can’t respond to this status request
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
SEE ALSO
For information about the low-level routine for terminating pending requests for a driver, see the next section, which describes the PBKillIO function.
1PBKillIO
You can use the PBKillIO function to terminate all current and pending I/O with a device driver.
FUNCTION PBKillIO (paramBlock: ParmBlkPtr;
async: Boolean) : OSErr;
paramBlock Contains a pointer to the parameter block.
async Specifies to the Device Manager whether the request is asynchronous. You should always set this parameter to FALSE.
You specify values and receive return values in a Device Manager parameter block.
<symbol\>\xae ioCompletion ProcPtr Pointer to completion routine
<symbol\>\xac ioResult OSErr Result code
<symbol\>\xae ioCRefNum Integer Driver reference number
DESCRIPTION
The PBKillIO function stops any current request being processed by the driver indicated by the ioCRefNum field and removes all pending requests from the driver’s request queue. The Device Manager calls the completion routine for each pending request with the ioResult field of each request equal to the result code abortErr.
The Device Manager always executes the PBKillIO routine immediately; that is, it never places a PBKillIO request in a driver’s request queue. However, you should not call this routine immediately—always call the PBKillIO routine synchronously.
Note that the Device Manager passes PBKillIO requests to a driver only if the driver is open and enabled for control calls.
The PBKillIO function terminates all pending requests for a driver, including requests initiated by other applications.<36pt\>\x12 <8bat\>s
SPECIAL CONSIDERATIONS
The PBKillIO function does not move memory. However, you should be sure the device driver does not move memory in response to this request before you call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the PBKillIO function is _KillIO ($A006). You must setup register A0 with the address of the parameter block. When _KillIO returns, register D0 contains the result code. Register D0 is the only register affected by this routine.
Registers on entry
A0 address of the parameter block
Registers on exit
D0 result code
RESULT CODES
noErr 0 No error
statusErr –18 Driver can’t respond to this status request
badUnitErr –21 Reference number out of range or not in use
unitEmptyErr –22 DCE points to null driver
notOpenErr –26 Driver isn’t open
SEE ALSO
For information about the high-level routine for terminating pending requests for a driver, see the previous section, which describes the KillIO function.
Summary of the Device Manager
This section lists the definitions and declarations provided in this chapter. You can find advanced Device Manager information in the “Summary” section of the chapter “Writing Your Own Device Driver,” in this book.
ioVRefNum : Integer; {volume reference or drive number}
CASE ParamBlkType OF
ioParam:
(ioRefNum: Integer; {driver reference number}
ioVersNum: SignedByte; {not used}
ioPermssn: SignedByte; {read/write permission}
ioMisc: Ptr; {not used}
ioBuffer: Ptr; {pointer to data buffer}
ioReqCount: LongInt; {requested number of bytes}
ioActCount: LongInt; {actual number of bytes}
ioPosMode: Integer; {positioning mode}
ioPosOffset: LongInt); {positioning offset}
cntrlParam:
(ioCRefNum: Integer; {driver reference number}
csCode: SignedByte; {type of request}
csParam: ARRAY[1..10] OF Integer);
{control or status info}
END;
END;
Routines
Opening and Closing Device Drivers
FUNCTION OpenDriver (name: Str255;
VAR refNum: Integer): OSErr;
FUNCTION PBOpen (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
FUNCTION OpenSlot (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
FUNCTION CloseDriver (refNum: Integer): OSErr;
FUNCTION PBClose (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
Reading From and Writing To Device Drivers
FUNCTION FSRead (refNum: Integer;
VAR count: LongInt;
buffPtr: Ptr): OSErr;
FUNCTION PBRead (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
FUNCTION FSWrite (refNum: Integer:
VAR count: LongInt;
buffPtr: Ptr): OSErr;
FUNCTION PBWrite (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
Controlling and Monitoring Device Drivers
FUNCTION Control (refNum: Integer;
csCode: Integer;
csParamPtr: Ptr): OSErr;
FUNCTION PBControl (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
FUNCTION Status (refNum: Integer;
csCode: Integer;
csParamPtr: Ptr): OSErr;
FUNCTION PBStatus (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
FUNCTION KillIO (refNum: Integer): OSErr;
FUNCTION PBKillIO (paramBlock: ParmBlkPtr;
async: Boolean): OSErr;
Result Codes
noErr 0 No Error
controlErr –17 Driver can’t respond to this control request
statusErr –18 Driver can’t respond to this status request
readErr –19 Driver can’t respond to read requests
writErr –20 Driver can’t respond to write requests
badUnitErr –21 Bad reference number
unitEmptyErr –22 Bad reference number
openErr –23 Driver can’t perform requested reading or writing
dInstErr –26 Couldn’t find driver in resource file
dRemovErr –26 Attempt to remove an open driver
notOpenErr –26 Driver isn’t open
abortErr –27 Request aborted by KillIO
ioErr –36 Data doesn’t match in read-verify mode
Assembly-Language Information
Device Manager Parameter Block
0 qLink used internally by the Device Manager
4 qType used internally by the Device Manager
6 ioTrap used internally by the Device Manager
8 ioCmdAddr used internally by the Device Manager
<symbol\>\xae 12 ioCompletion completion routine
<symbol\>\xac 16 ioResult result code
<symbol\>\xae 18 ioNamePtr driver name
<symbol\>\xae 22 ioVRefNum drive number
<symbol\>\xae 24 ioRefNum driver reference number
26 ioVersNum not used
<symbol\>\xae 28 ioPermssn read/write permission
30 ioMisc not used
<symbol\>\xae 32 ioBuffer pointer to data buffer
<symbol\>\xae 36 ioReqCount requested number of bytes
<symbol\>\xac 40 ioActCount actual number of bytes
<symbol\>\xae 44 ioPosMode positioning mode
<symbol\>\xab 46 ioPosOffset positioning offset
<symbol\>\xae 24 ioCRefNum driver reference number
<symbol\>\xae 26 csCode type of control or status request
<symbol\>\xab 28 csParam control or status information
28 ioMix reserved for use by slot driver’s open routine
<symbol\>\xae 32 ioFlags determines number of additional fields for OpenSlot
<symbol\>\xae 34 ioSlot slot number
<symbol\>\xae 35 ioId slot resource ID
<symbol\>\xae 34 ioSEBlkPtr pointer to external parameter block
Trap Macros
Trap Macro Trap Word
_Open $A000
_Close $A001
_Read $A002
_Write $A003
_Control $A004
_Status $A005
_KillIO $A006
Writing Your Own
Device Driver
Writing a Device Driver2-3
How the Device Manager Works2-3
Creating a Driver Resource2-5
Responding to the Device Manager2-10
Entering and Exiting From Driver Subroutines2-10
Writing the Open and Close Subroutines2-11
Writing the Prime Subroutine2-14
Writing the Control and Status Subroutines2-15
Handling I/O Asynchronously2-18
Installing the Driver2-19
Writing a Chooser-Compatible Device Driver2-22
How the Chooser Works2-22
Creating a Chooser Extension File2-25
Creating a Device Package2-27
Responding to the Chooser2-29
Allocating private storage2-32
Writing a Desk Accessory2-33
How Desk Accessories Work2-33
Creating a Driver Resource for a Desk Accessory2-34
Opening and Closing a Desk Accessory2-34
Responding to Events2-35
Reference2-36
Data Structures2-36
The Extended Device Control Entry2-37
Routines2-38
Resources2-41
Summary2-43
2Writing Your Own Device Driver
This chapter describes how to write a device driver, how your device driver can use the Chooser, and how to make your device driver into a desk accessory.
Although this chapter describes some of the internal mechanisms of the Device Manager, you do not need to read this chapter if you want to use the Device Manager to communicate with an already-existing device driver. See the chapter “The Device Manager” instead.
If you do want to write your own device driver, or just learn about how the Device Manager interacts with device drivers, you should first read the chapter “The Device Manager” and then read this chapter.
This chapter shows how you can
n create a device driver that responds to Device Manager requests
n provide a user interface for your driver with the Chooser
n create a desk accessory
You should read the first section of this chapter, which describes how to write a basic device driver, before reading the sections on the Chooser or desk accessories.
Writing a Device Driver
This section shows you how to write a basic device driver—one that can respond to Device Manager requests. You should read this section before reading “Writing a Chooser-Compatible Device Driver” on page 2-21 or “Writing a Desk Accessory” on page 2-31.
To write a device driver, you need to know
n how the Device Manager opens drivers, maintains data structures about them, and communicates with them
n how to create a driver resource
n how the code in your driver resource should respond to Device Manager requests
The next few sections examine these topics.
How the Device Manager Works
The Device Manager communicates with device drivers by calling one of five driver subroutines: Open, Prime, Control, Status, and Close. All drivers must have an Open and Close subroutine; the Prime, Control, and Status subroutines are optional.
Each driver subroutine is responsible for handling certain types of Device Manager requests.
The Device Manager routines are listed by their actual names. The corresponding driver subroutine names are descriptive names. You can choose any name for your actual driver routines—the Device Manager locates them using offsets you provide.<36pt\>\x12 <8bat\>u
When an application or another manager calls the OpenDriver function, the Device Manager searches the unit table to see if a driver with the specified name is already open. If no open driver has a matching name, the Device Manager opens the driver resource with the specified name. To open a driver from a resource, the Device Manager
n loads the driver code from the resource into memory
n creates a device control entry for it, filling it in with values from the header of the driver resource,
n installs the device control entry in the unit table at a location determined by the driver resource ID
n calls the driver’s Open subroutine.
The header of the driver resource contains information about the driver such as which driver subroutines are implemented and where the subroutines are. See “Creating a Driver Resource” on page 2-5 for the format of the driver header.
Unlike the OpenDriver function, the PBOpen and OpenSlot functions expect the driver to be already installed. These functions search the unit table for the driver’s device control entry and call the driver’s Open subroutine.
If you use the PBOpen or OpenSlot function to open your driver, you are responsible for creating the device control entry and installing it in the unit table. Even if you use the OpenDriver function to install your driver, you are responsible for finding an empty space in the unit table, which may require allocating more memory for it. The section “Installing the Driver” on page 2-18 discusses the device control entry, the unit table, and how you can properly install your driver.
To allow asynchronous requests, the Device Manager maintains a driver request queue for each open driver. A driver request queue is a standard Operating System queue, as described in the chapter “Operating System Utilities” in the Inside Macintosh: Operating System volume.
When an application or another manager calls the PBRead, PBWrite, PBStatus, or PBControl routine, the Device Manager places the parameter block at end of the driver’s request queue. The high-level versions of these routines create a parameter block (filling the fields with the values of the parameters sent to routine where possible and using default values for the rest) and add it to the request queue. As previously-queued requests are satisfied, the parameter block moves forward in the request queue. When the parameter block is at the beginning of the request queue, the Device Manager calls the appropriate driver subroutine and passes it a pointer to the parameter block and a pointer to the driver’s device control entry.
For reading and writing requests, the Device Manager calls the Prime subroutine, described in “Writing the Prime Subroutine” on page 2-13. This subroutine can execute synchronously, completing the requested reading or writing before returning control to the Device Manager, or asynchronously, beginning the requested transaction but returning control to the Device Manager before the completing it. If the driver’s Prime routine can execute asynchronously, the driver must use some mechanism to regain control of the processor and complete the request. Typically these drivers provide interrupt handlers that respond to interrupts from the driver’s device and notify the Device Manager when the transaction is complete. See “Handling I/O Asynchronously” on page 2-17 for more details.
The Device Manager handles controlling and monitoring requests in the same way as reading and writing requests, except that for controlling requests it calls the Control subroutine and for monitoring requests it calls the Status subroutine. The section “Writing the Control and Status Subroutines” on page 2-15 shows how to provide these subroutines for your driver.
The Device Manager responds to KillIO requests by removing the parameter blocks from the queue, calling their completion routines with the result code abortErr, and then calling the drivers Control subroutine with a value of killCode for the csCode parameter. You can find information on handling KillIO requests in “Writing the Control and Status Subroutines” on page 2-15.
In response to a closing request, the Device Manager queues the parameter block, making one if necessary. Since closing requests cannot be asynchronous, the Device Manager sends each queued request to the driver. Since the last request is the closing request, the Device Manager ultimately calls the Close subroutine. When the driver indicates it has satisfied the closing request, the Device Manager unlocks the device control entry and the driver code. Note that the Device Manager does not dispose of the device control entry or even remove it from the unit table.
The section “Writing the Open and Close Subroutines” on page 2-11 shows how your driver can respond to both opening and closing requests.
Creating a Driver Resource
You will probably want to store your driver in a driver resource, although if you are writing a driver for a slot device you might want to store your driver in the declaration ROM of the expansion card (see Designing Cards and Drivers for the Macintosh Family for more details). Storing your driver in a driver resource allows the OpenDriver function to load your driver code into memory and install a device control entry for your driver in the unit table.
Like all resources, your driver resource has a resource type, a resource ID, a resource name, and resource attributes:
n The resource type must be 'DRVR'.
n The resource ID determines where in the unit table the OpenSlot function installs the driver’s device control entry. Unfortunately, you must choose the resource ID when creating your driver resource and you cannot know which unit numbers will be available until you open your driver. Therefore, your driver-opening routine must find an empty location in the unit table and change the resource ID accordingly. The section “Installing the Driver” on page 2-18 discusses appropriate values for the resource ID.
n The resource name should be the same as the driver name so that the OpenDriver function can find the driver resource. A driver name consists of a period (.) followed by any sequence of 1 to 254 printing characters. The Device Manager ignores case (but not diacritical marks) when comparing names.
n The resource attributes of your driver resource depend on your driver. A typical driver might have these attributes: locked, since most drivers contain code that is called at interrupt time; in the system heap, so that the driver exists over launches of applications; and preloaded, which makes resource loading slightly more efficient.
A driver resource has two parts:
n a driver header that contains information about the driver
n the routines that do the work of the driver
The driver header contains a few words of flags and other data, offsets to the driver’s routines, and an optional driver name. Figure 2-1 shows the format of a driver header.
The format of a driver header
drvrFlags Contains flags in its high-order byte that specify certain characteristics of the driver. You can use the constants in Listing 2-1 to index the flags in this field.
drvrDelay Indicates number of ticks between periodic actions.
drvrEMask Used for desk accessories. See “Writing a Desk Accessory” on page 2-31 for information about this field.
drvrMenu Used for desk accessories. See “Writing a Desk Accessory” on page 2-31 for information about this field
drvrOpen Contains an offset to the Open driver subroutine.
drvrPrime Contains an offset to the Prime driver subroutine.
drvrCtl Contains an offset to the Control driver subroutine.
drvrStatus Contains an offset to the Status driver subroutine.
drvrClose Contains an offset to the Close driver subroutine.
drvrName Contains the driver name. You can use uppercase and lowercase letters when naming your driver, but the first character should be a period—.YourDriver, for example.
See the section “Entering and Exiting From Driver Subroutines” on page 2-9 for more information about the subroutine offsets.
Your driver routines, which follow the driver header, must be aligned on a word boundary.<36pt\>\x12 <8bat\>u
The index constants for the drvrFlags field
dReadEnable EQU 0 ;set if driver can respond to Read requests
dWritEnable EQU 1 ;set if driver can respond to Write requests
dCtlEnable EQU 2 ;set if driver can respond to Control requests
dStatEnable EQU 3 ;set if driver can respond to Status requests
dNeedGoodbye EQU 4 ;set if driver needs to be called before the
; application heap is initialized
dNeedTime EQU 5 ;set if driver needs time for performing
; periodic tasks
dNeedLock EQU 6 ;set if driver will be locked in memory as
; soon as it's opened
The first four flags, which occupy bits 0–3 of the high-order byte (bits 8–11 of the drvrFlags word), indicate which Device Manager requests the driver’s routines can respond to. The next section, “Responding to the Device Manager,” on page 2-9 describes these routines in detail.
Drivers in the application heap are lost when the heap is reinitialized. If you set the dNeedGoodbye flag, the Device Manager calls your driver before the heap is reinitialized so that you can perform any clean-up actions. See “Writing the Control and Status Subroutines” on page 2-15 for more details.
You set the dNeedTime flag of the drvrFlags word if your device driver needs to perform some action periodically. For example, a network driver may want to poll its input buffer every 5 seconds to see if it has received any messages. The value of the drvrDelay word indicates how many ticks should pass between periodic actions. For example, a value of 0 in the drvrDelay word indicates that the action should happen as often as possible, a value of 1 means it should happen every sixtieth of a second, a value of 2 means at most every thirtieth of a second, and so on. Whether the action actually occurs this frequently depends on how often the application calls the Desk Manager procedure SystemTask. See “Writing the Control and Status Subroutines” on page 2-15 for more details.
If you do not want your driver to depend on applications to call SystemTask, you can still perform actions periodically by installing a VBL task, a Deferred Task Manager task, a Time Manager task, or a Notification Manager task. For more information, see the chapters corresponding to these managers.<36pt\>\x12 <8bat\>u
You need to set the dNeedLock flag if your driver’s code should be locked in memory. In particular, you need to set this flag in these two cases:
n If any part of your driver’s code can be called at interrupt time. At interrupt time, the heap may not be in a consistent state. If your driver is not locked, the system could be in the process of relocating it in memory.
n If your driver provides the Operating System with a pointer to any part of its code. For example, if your driver uses the Device Manager to call another driver, you might provide the Device Manager with a pointer to completion routine. If that completion routine is in your driver code, your driver code must be locked. Otherwise, that pointer might not be valid by the time the Device Manager calls the completion routine.
You can create your driver header in these ways:
n You can use a resource compiler. See “Resources” on page 2-38 for the Rez format of the driver resource.
n You can use the DC instruction, as shown in Listing 2-2, to position the header information directly in your assembly language code.
An assembly-language driver header
DHeader
DFlags DC.B 0 ;set by DRVROpen
DC.B 0 ;set by DRVROpen
DDelay DC.W 0 ;none
DEMask DC.W 0 ;DA event mask
DMenu DC.W 0 ;no menu
DC.W DOpen - DHeader ;offset to Open
DC.W DPrime - DHeader ;offset to Prime
DC.W DControl - DHeader ;offset to Control
DC.W DStatus - DHeader ;offset to Status
DC.W DClose - DHeader ;offset to Close
Name DC.B '.DRIVER' ;name of driver
ALIGN 2 ;word align
In this example, the drvrFlags word is cleared to 0—the actual flags are set in the DRVROpen routine, defined in “Writing the Open and Close Subroutines” on page 2-11. The drvrDelay field is set to 0 and the drvrEMask and drvrMenu fields are set to 0, as this driver is not a desk accessory. The next five fields give offsets to the driver subroutines, defined in the next section, “Responding to the Device Manager.” The header ends with the driver name and word alignment.
Responding to the Device Manager
The Device Manager calls a driver subroutine by setting up registers and jumping to the address indicated by the subroutine’s offset in the driver header.
n Register A0 contains a pointer to the parameter block.
n Register A1 contains a pointer to the device control entry.
This interface requires you to use some assembly language when writing a driver. However, you can write your driver subroutines in a high-level language if you provide an assembly-language dispatching mechanism that acts as an interface between the Device Manager and your driver subroutines.
The next few sections discuss how you can provide a dispatching routine and how you can implement your driver subroutines in a high-level language.
Entering and Exiting From Driver Subroutines
Listing 2-3 shows an assembly-language routine that you can use an interface between the Device Manager and your high-level language driver subroutines. In this example, each driver subroutine (which the Device Manager locates using the offsets defined in Listing 2-2) pushes the address of its corresponding high-level language subroutine and then branch to the dispatching routine.
Entering and exiting from driver subroutines
DOpen pea DRVROPEN
bra.s DRVRDispatch
DPrime pea DRVRPRIME
bra.s DRVRDispatch
DControl pea DRVRCONTROL
bra.s DRVRDispatch
DStatus pea DRVRSTATUS
bra.s DRVRDispatch
DClose pea DRVRCLOSE ;and fall thru to DRVRDispatch
DRVRDispatch
movem.l a0/a1, -(sp) ;save registers (for IODone)
clr.w -(sp) ;save room for result
move.l a0, -(sp) ;push paramblock ptr on stack
move.l a1, -(sp) ;push dce ptr on stack
movea.l $12(sp), a0 ;load address of routine into a0
The DRVRDispatch routine begins by saving the A0 and A1 registers, clearing room on the stack for the result code, and pushing the parameters on the stack and jumping to the appropriate driver subroutine.
When the driver subroutine returns, the DRVRDispatch routine moves the result code to register D0, restores the A0 and A1 registers, clears the subroutine address from the stack, and then returns control to the Device Manager in one of two ways:
n With an RTS instruction. You do not want the Device Manager to remove to the request from the driver request queue. In particular, the DRVRDispatch routine returns with an RTS instruction from immediate requests (which the Device Manager never adds to the queue) and from incomplete requests that the driver is executing asynchronously (which should remain in the queue until the driver completes them). The driver subroutines return a result code of 1 to indicate that the request is still incomplete.
n By jumping to the IODone routine. This routine, described in detail on page 2-38, indicates to the Device Manager that the request is complete. The Device Manager will remove the request from the driver request queue and call the completion routine.
The next few sections discuss how to write your driver subroutines.
Writing the Open and Close Subroutines
You must provide both an Open subroutine and a Close subroutine for your driver. The Device Manager always executes these routines as synchronous requests and they should return by means of an RTS instruction.
Your Open routine must allocate any private storage that your driver requires and store a handle to this storage in the dCtlStorage field of the device control entry.
After allocating private storage, you should initialize it and do any further preparation required by your driver.
You might also install interrupt handlers, change interrupt vectors, and store a pointer to the device control entry somewhere in local storage for the interrupt handlers to use. The section “Handling I/O Asynchronously” on page 2-17 discusses interrupt handling in more detail.
The DrvrDispatch routine in Listing 2-3 returns the result codes of the driver subroutines in register D0. However, upon return from the Open subroutine, the Device Manager sets register D0 to 0; therefore, your Open routine should place the result code in the ioResult field of the parameter block.
Listing 2-4 shows a sample Open subroutine, DRVROpen. The DRVROpen function begins by setting the flags of the device control entry (see Listing 2-2 on page 2-9) and checking whether the driver is already open (by examining the contents of the dCtlStorage field of the device control entry). If the driver is not already open, the DRVROpen function allocates memory for private storage. The private storage of the driver in this example contains two fields: the numBeeps field stores a value of type LongInt, and the buffer field stores a buffer of 255 bytes. The Prime, Control, and Status subroutines described in the next two sections use these fields.
If the DRVROpen function fails to allocate memory for private storage, it returns with an openErr result code. The DRVRDispatch routine returns this result code in register D0, which notifies the Device Manager that the driver did not open. If the memory allocation succeeds, the DRVROpen function places a noErr result code in the ioResult field of the parameter block.
Your Close subroutine must reverse the effects of your Open subroutine by releasing any used memory, removing interrupt handlers, removing any VBL or Time Manager tasks, and replacing changed interrupt vectors.
If you want to save any information about the operational state of the driver until the next time the driver is opened, you should store this information in a relocatable block of memory pointed to by the dCtlStorage field, because the Device Manager does not dispose of the device control entry when a driver is closed.
Listing 2-5 shows a sample Close subroutine, DRVRClose. Since the DRVROpen function for this sample driver allocated private storage, and the driver doesn’t need to store any information until the next time its opened, the DRVRClose function simply disposes the private storage.
Sample Close driver subroutine
FUNCTION DRVRClose (VAR pb: ParamBlockRec;
VAR dce: DCtlEntry): OSErr;
BEGIN
IF (dce.dCtlStorage <> nil) THEN
BEGIN
DisposHandle(dce.dCtlStorage);
dce.dCtlStorage := nil;
END;
DRVRClose := NOErr;
END;
Writing the Prime Subroutine
Your Prime routine implements the I/O requests. You can write your Prime subroutine to execute synchronously or asynchronously. A synchronous Prime subroutine completes an entire I/O request before returning to the Device Manager. An asynchronous Prime subroutine can begin an I/O transaction but return to the Device Manager before the request is complete. In this case, the I/O request continues to be executed, typically when more data is available, by other routines such as interrupt handlers or completion routines. The section “Handling I/O Asynchronously” on page 2-17 discusses how to complete an asynchronous Prime subroutine.
Notice the difference between asynchronous requests and asynchronous subroutines. The Device Manager places asynchronous requests in the request queue and returns control to the application right away. Asynchronous subroutines are capable of satisfying a large request in small pieces, one piece at a time.<36pt\>\x12 <8bat\>u
The Device Manager indicates whether it is requesting a Read or a Write operation by placing one of the following constants in the low-order byte of the ioTrap field of the parameter block:
aRdCmd EQU 2 ;Read operation requested
aWrCmd EQU 3 ;Write operation requested
The Device Manager provides two routines—Fetch and Stash—that provide low-level support for reading and writing characters to and from data buffers. Use of these routines is optional. The section “Routines” on page 2-36 describes these routines in detail.
The Fetch and Stash routines update the ioActCount field of the parameter block. If you do not use these routines, you are responsible for updating this field.
If your driver serves a block device, you should update the dCtlPosition field of the device control entry.
Listing 2-4 shows a sample Prime subroutine, DRVRPrime. The DRVRPrime function allows applications to read and write up to 255 bytes of data to its private data buffer.
The DRVRPrime function checks the requested byte count and lowers it if it is greater than the size of the private buffer. Then, the function examines the low-order byte of the ioTrap field of the parameter block to determine whether the Device Manager is requesting a read operation or a write operation. The function then moves the requested number of from its private storage to the buffer designated by the parameter block for a read operation, or from the designated buffer to its private storage for a write operation. (A real driver would probably send the data to or request the data from a hardware device, instead of storing the data itself.)
The DRVRPrime function then updates the ioActCount field appropriately.
Sample Prime driver subroutine
FUNCTION DRVRPrime (VAR pb: ParamBlockRec;
VAR dce: DCtlEntry): OSErr;
VAR
dStore : MyDriverGlobalsHdl;
callType : Integer;
numBytes : LongInt;
BEGIN
DRVRPrime := NoErr;
IF (dce.dCtlStorage <> nil) THEN
BEGIN
dStore := MyDriverGlobalsHdl(dce.dCtlStorage);
numBytes := pb.ioReqCount;
IF numBytes > 256 THEN
numBytes := 256;
callType := BAND($00FF,pb.ioTrap);
CASE callType OF
aRdCmd: BlockMove(@dStore^^.buffer,
pb.ioBuffer,numBytes);
aWrCmd: BlockMove(pb.ioBuffer,
@dStore^^.buffer,numBytes);
END;
pb.ioActCount := numBytes;
END
ELSE
DRVRPrime := mFulErr;
END;
Writing the Control and Status Subroutines
Like the Prime subroutine, the Control and Status subroutines that you write can execute synchronously or asynchronously.
The Device Manager passes information to the Control routine in the csCode and csParam fields of the parameter block. The csCode field specifies the type of control information and the csParam field, which is up to 22 bytes long, contains the actual control information. The Device Manager reserves some values for the csCode parameter to indicate certain types of control requests.
You can identify these requests by checking the csCode field for these constant values:
Constant name Value Meaning
killCode 1 KillIO requested
goodBye -1 Heap being reinitialized
accRun 65 Time for periodic action
When the Device Manager receives a KillIO request, it removes every parameter block from the driver request queue. If your driver responds to any requests asynchronously, the part of your driver that completes requests begun by the driver subroutines (for example, an interrupt handler) might expect the parameter block for the pending request to be at the head of the request queue. The Device Manager notifies your driver of KillIO requests so that you can take the appropriate actions to stop work on the pending request. You should be certain you return control to the Device Manager by means of an RTS instruction and not by jumping to the IODone routine.
If you set the dNeedGoodbye flag in the drvrFlags word of your device header, the Device Manager will call your Control routine with the value goodBye in the csCode parameter before the heap is reinitialized. You can respond by performing any clean-up actions necessary before heap reinitialization.
If you set the dNeedTime flag in the drvrFlags word of your device header, the Device Manager will call your Control routine with the value accRun in the csCode parameter periodically.
You can interpret other values of the csCode field as suited for your driver. Your Control routine should accept this control information and manipulate the device as requested.
Listing 2-7 shows a sample Control subroutine, DRVRControl. The DRVRControl function allows applications to specify a number of times for the driver to produce a system beep. The driver stores the requested number in its private storage.
The DRVRControl function interprets the value
CONST beepIt = 100;
in the csCode field to indicate a request for system beeps. You would define this constant in the constant definition part of your Pascal unit.
The DRVRControl function examines the csCode field of the parameter block for the beepIt value. If it finds this value, the function examines the csParam field for the number of beeps requested, stores the requested number in private storage, and produces the system beeps.
Sample Control subroutine
FUNCTION DRVRControl (VAR pb: ParamBlockRec;
VAR dce: DCtlEntry): OSErr;
VAR
dStore : MyDriverGlobalsHdl;
beepTimes : Integer;
BEGIN
IF (dce.dCtlStorage <> nil) THEN
BEGIN
dStore := MyDriverGlobalsHdl(dce.dCtlStorage);
CASE pb.csCode OF
beepIt: BEGIN
beepTimes := pb.csParam[0];
dStore^^.numBeeps:= dStore^^.numBeeps+beepTimes;
REPEAT
SysBeep(1);
beepTimes := beepTimes - 1;
UNTIL (beepTimes = 0);
END;
END;
DRVRControl := NoErr;
END;
END;
The Status subroutine should work in a similar manner. The Device Manager uses the csCode field to specify the type of status information requested. Your Status routine should be prepared to respond to whatever requests are appropriate for your driver and return the information in the csParam field.
The Device Manager does not reserve any csCode values for the Status subroutine. However, when the Device Manager receives a Status request with a csCode value of 1, it returns a handle to the driver’s device control entry from the unit table. The driver’s Control subroutine never sees this request.
Listing 2-7 shows a sample Status subroutine, DRVRStatus. The DRVRStatus function returns the number of beeps requested by the most recent Control request.
This function interprets the value
CONST getBeeps = 100;
in csCode field to indicate a Status request for number of beeps last requested. You would define this constant in the constant definition part of your Pascal unit.
Sample Status subroutine
FUNCTION DRVRStatus (VAR pb: ParamBlockRec;
VAR dce: DCtlEntry): OSErr;
VAR
dStore : MyDriverGlobalsHdl;
BEGIN
IF (dce.dCtlStorage <> nil) THEN
BEGIN
dStore := MyDriverGlobalsHdl(dce.dCtlStorage);
CASE pb.csCode OF
getBeeps:
BEGIN
pb.csParam[0] := dStore^^.numBeeps;
END;
END;
END;
DRVRStatus := NoErr;
END;
Handling I/O Asynchronously
If you write any of your driver subroutines to execute asynchronously, you must provide a mechanism for your driver to complete the requests.
Some examples of routines that you might use are:
n Completion routines. Your driver subroutine could call another driver to start the data transfer. In this case, you can provide that driver with a completion routine. When the other driver completes the request, the Device Manager executes the completion routine. In the completion routine, you could call the other driver again to execute the next part of the I/O operation. When the entire operation is complete, the completion routine should return by jumping to the IODone routine.
n Interrupt handlers. Your driver might serve a device, such as a slot device, that generates interrupts. An interrupt causes the processor to suspend normal execution, save the address of the next instruction and the processor’s internal status on the stack, and execute an interrupt handler. You can create an interrupt handler that continues an I/O operation when the device signals, by an interrupt, that it is ready to continue. Your interrupt handler must preserve any registers it uses and clear the source of the interrupt. You may want your Open routine to store a handle to your driver’s device control entry in your driver’s local storage. You can get a handle to the device control entry using the GetDCtlEntry function. For more information on Macintosh interrupts and how to install your own interrupt handlers, see the Guide to Macintosh Family Hardware and Designing Cards and Drivers for the Macintosh Family.
n VBL, Time Manager, and Deferred Task Manager tasks. Installing any of these tasks ensures that your driver receives system time at some point in the future. During this time, you can check to see if the I/O operation is ready to continue.
Interrupts can occur while your interrupt handler is executing. When this happens, it’s possible for the system to suspend your interrupt handler for the first interrupt and then reenter your interrupt handler for the second interrupt. Your interrupt handler is reentrant if it handles this situation correctly.
To help ensure that your driver is reentrant, your driver should store a handle to its private storage in the dCtlStorage field of the device control entry, and not use memory within the driver code itself.u
If your driver serves a device on an expansion card, you might want to respond to slot interrupts, or interrupts signalled by devices on expansion cards. When a NuBus card device signals a slot interrupt, the CPU can quickly detect which card requested the interrupt service, but not which device on the card. To determine which device caused the interrupt, the system uses a polling procedure.
If your driver serves a NuBus card device, it should provide a polling routine—a routine that checks if the device it serves caused the current interrupt, and, if so, calls the proper driver code to handle the interrupt. The Slot Manager maintains a queue of these polling routines for each slot. Your driver can install an element in this queue using the Slot Manager routine SIntInstall. You can also remove a queue element with the Slot Manager routine SIntRemove. The chapter “The Slot Manager” in this book gives more information about the SIntInstall and SIntRemove routines.
For more information on slot interrupts and writing drivers for slot devices, see Designing Cards and Drivers for the Macintosh Family.
Installing the Driver
When you have completed your driver resource, you have a number of options for storing it and installing the driver. Two of the most common are:
n Storing the driver resource in an application and having the application install it.
n Storing the driver resource in a system extension file and writing an initialization ('INIT') resource to install it.
In either case, you need to write code to ensure your driver is properly installed and opened before making sending it any requests. You can create a device control entry yourself and install it in the unit table and then use the PBOpen or OpenSlot function to open it, or you can use the OpenDriver function to install and open it.
If you want to install the driver yourself, see the section “Data Structures” on page 2-34 for details about the device control entry data type.
If you want to use the OpenDriver function to install your driver, you are responsible for examining the unit table and changing your driver resource ID so that the OpenDriver function install your driver in an empty location in the unit table. The OpenDriver function uses the following formula to determine a drivers unit number:
The unit table is a 256-byte nonrelocatable block of 64 four-byte spaces that contain handles to device control entries for installed drivers. If needed, the Device Manager automatically expands the unit table to a maximum of 128 entries. The global variable UTableBase contains the base address of the unit table.
For an entry in the unit table to be suitable it must be empty—that is, contain a NIL pointer—and it must not be reserved. Table 2-1 shows a summary list of unit numbers reserved for specific purposes:
Reserved unit numbers
Unit Number Range RefNum Range Purpose
0 through 11 -1 through -12 Serial, Disk, AppleTalk,
Printer Drivers
12 through 31 -13 through -32 Desk Accessories
32 through 39 -33 through -40 SCSI Devices
40 through 47 -41 through -48 Reserved
48 through 127 -49 through -128 Slot and Other Drivers
Listing 2-9 gives a high-level language function for searching the unit table for an appropriate location to install your driver. The driver-opening routine in the chapter “The Device Manager” calls this function and then uses the OpenDriver function to install and open the device driver.
The Chooser is a desk accessory that helps provide a standard user interface for device drivers. It allows device drivers to prompt the user for choices such as which serial port to use, which AppleTalk zone to communicate with, and which LaserWriter to use.
If your device driver can serve different hardware devices or configurations, you may want to use the Chooser to provide an interface for the user to make choices.
You should read the previous section, “How the Device Manager Works,” which begins on page 2-3, before you read this section.
This section describes the Chooser, how it works, and how you can provide resources that set up a Chooser interface and respond to actions from the user.
How the Chooser Works
The Chooser allows users to select which devices they want to use. When the user launches the Chooser desk accessory, the Chooser displays the Chooser window, which contains lists and buttons for making device-related choices. Typically, users select a type of device from the icon list, then select the particular device they want to use from the device list. For AppleTalk devices, they must also select an AppleTalk zone from the zone list. The Chooser window can also display buttons, such as an OK button, and radio buttons, such as the LaserWriter’s background printing On and Off buttons.
Figure 1-4 shows an example Chooser window.
The Chooser window
The Chooser relies heavily on the List Manager for creating, displaying, and manipulating possible user selections in this window. You may want to read the List Manager chapter in the Inside Macintosh: Toolbox volume for more information.
The Chooser does not communicate directly with device drivers; instead, it communicates with device packages. A device package is a resource similar to a driver resource, except a device package responds to Chooser messages instead of Device Manager requests. The device package is responsible for communicating the user’s choices to the device driver.
Device packages are stored in Chooser extension files, which the Chooser expects to find in the System Folder of the user’s startup disk. The Chooser extension file contains a number of resources in addition to the device package resource. These other resources allow you to specify information to the chooser. For example, you can specify
n The device list box label. The Chooser displays this label over the device list.
n Which buttons to use. The Chooser allows the device package to display up to four buttons, called the Left button, the Right button, the On radio button, and the Off radio button.
n The titles and positions of the buttons.
n The radio button label.
n The AppleTalk device type name. The Chooser searches the current AppleTalk zone for devices of this type.
n An AppleTalk Name-Binding Protocol (NBP) retry interval and a timeout count. The Chooser uses this information when searching for AppleTalk devices.
When a user selects the icon corresponding to a particular device package, the Chooser sends messages to that device package by calling the device package as if it were the following function:
FUNCTION Package (message, caller: Integer;
objName, zoneName: StringPtr;
p1, p2: LongInt) : OSErr;
Like the driver subroutine names, the name Package for the device package routine is a descriptive name. You can choose any name for your actual device package routine—the Chooser locates the routine using the device package header you provide. <36pt\>\x12 <8bat\>u
Parameter descriptions
message Identifies the operation to be performed and has one of the following constant values:
initMsg = 11; {user selected this device package}
newSelMsg = 12; {user made new device selections}
fillListMsg = 13; {fill the device list with choices}
getSelMsg = 14; {mark one or more choices selected}
selectMsg = 15; {user made a selection}
deselectMsg = 16; {user cancelled a selection}
terminateMsg = 17; {allows device package to clean up}
buttonMsg = 18; {user selected a button}
The section “Responding to the Chooser” on page 2-27 explains these messages in more detail.
caller Identifies the caller. A value of 1 indicates the Chooser. Values in the range 0 to 127 are reserved; values outside this range may be used by applications.
zoneName Contains name of AppleTalk zone containing the devices in the device list. If the Chooser is being used with the local zone and bit 24 of the flags field of the device package header is not set, the string value is '*'; otherwise, it’s the actual zone name. See the section “Creating a Device Package” on page 2-26 for more information about the package header.
p1 Contains a handle to the List Manager list that contains the device choices displayed in the device list box.
objName Contains other information. The meaning of this parameter depends on the value of the message parameter. See “Responding to the Chooser” on page 2-27 for more information
p2 Contains other information. The meaning of this parameter depends on the value of the message parameter. See “Responding to the Chooser” on page 2-27 for more information
When the user opens the Chooser, the Chooser searches the Extensions folder in the System Folder of the startup disk for Chooser extension files. For each one it finds, it opens the file, fetches the device’s icon, fetches the flags field from the device package header, and closes the file. The Chooser then displays each device’s icon, graying the icons for AppleTalk devices if AppleTalk is not connected.
When the user selects a device package icon that is not grayed, the Chooser reopens the corresponding Chooser extension file and does the following:
1. The Chooser labels the device list box with the device list box label.
2. The Chooser sends the init message to the selected device package.
3. If the selected device package represents a serial printer, the Chooser places in the device list box the two icons that represent the printer port and the modem port serial drivers. When the user makes a selection, the Chooser records the user’s choice in low memory and parameter RAM.
4. If the selected device package represents an AppleTalk device and does not accept fillList messages, the Chooser initiates an asynchronous routine that interrogates the current AppleTalk zone for all devices whose type matches the AppleTalk device type name specified in the Chooser extension file. The asynchronous routine uses the retry interval and the timeout count. As responses arrive, the Chooser updates the device list box.
5. If the selected device package does accept fillList messages, the Chooser sends the fillList message to the device package. The device package responds by filling the device list with the appropriate device choices.
6. To determine which entries in the device list should be selected, the Chooser calls the device package with the getSel message. The device package responds by inspecting the list and setting the selected or unselected state of each entry. The Chooser may send the getSel message frequently; for example, each time a new response to the AppleTalk zone interrogation arrives. The Chooser does not send the getSel message for serial printers; it highlights the icon corresponding to the currently-selected serial port, as recorded in low memory.
7. If the device package allows multiple devices to be active at once, the Chooser sets the appropriate List Manager bits. When the user selects or deselects a device, the Chooser calls the device package with the appropriate message. For packages that do not accept multiple active devices, the Chooser sends the select or deselect message; otherwise, it sends the newSel message. The device package mounts or unmounts the device, if appropriate, and records the user choice.
8. When the user chooses a different device type or closes the Chooser, the Chooser calls the current device package with the terminate message, if the package accepts this kind of message. At this time, the package can clean up, if necessary. The Chooser then calls the UpdateResFile procedure, closes the device resource file, and flushes the system startup volume.
Creating a Chooser Extension File
The Chooser accepts three types of Chooser extension files to identify different kinds of devices:
File type Device type
'PRES' Serial printer
'PRER' Non-serial printer
'RDEV' Other device
You can specify the creator of your device resource file, which allows you to give your device its own icon.
You can include these resources in your device package file:
Resource Type and ID Description
'PACK' –4096 Device package. This resource contains the device
package header and code.
'STR ' –4096 Type name for AppleTalk devices. The Chooser searches
the current Appletalk zone for devices of this type.
'GNRL' –4096 Appletalk information. The first byte of this resource
contains the Name-Binding Protocol (NBP) retry interval,
the second byte contains the timeout count.
'STR ' –4091 List box label. The Chooser labels the device list with this
string after the user has selected the device’s icon.
'STR ' –4087 Radio button label.
'STR ' –4088 Off radio button title.
'STR ' –4089 On radio button title.
'STR ' –4092 Right button title.
'STR ' –4093 Left button title.
'ncrt' –4096 Button positions.
'LDEF' –4096 Alternate list definition procedure. You can supply this
procedure to modify the device list—to include pictures
or icons, for example.
'STR ' –4090 Reserved for use by the Chooser.
You should also include a 'BNDL' resource to give your device type a distinctive icon, because this may be the only way that devices are identified in the Chooser window. The Finder Interface chapter describes the 'BNDL' resource.
The next section, “Creating a Device Package,” discusses the device package resource in more detail, and the section “How the Chooser Works” on page 2-21 describes how the Chooser uses the fields related to AppleTalk.
The Chooser window allows your device package to display two buttons, called the Left button and the Right button because of their default positions. The left button has a double border and is highlighted (the title string is dark, not gray) when one or more devices are selected from the device list. When this button is highlighted, pressing Return or Enter, or double-clicking in the device list are equivalent to clicking the button. The right button has a single border and is always highlighted, and the user can activate it only by clicking it.
The Chooser also allows you to display two radio buttons and a radio button label. These buttons are called the On radio button and the Off radio button because those are the titles the LaserWriter uses, but you can give them whatever titles you want.
You can position these buttons by including a resource of type 'ncrt' with an ID of –4096. The first word in this type of resource specifies the number of rectangles, and the rest of the resource contains the rectangle definitions. The first rectangle positions the left button, the second positions the right button, the third positions the On radio button, and the fourth positions the Off radio button. The fifth rectangle positions the radio button label.
Each rectangle definition is 8 bytes long and contains the rectangle coordinates in the order [top, left, bottom, right]. The default values are [112, 206, 132, 266] for the left button and [112, 296, 132, 356] for the right button. You could use the values [112, 251, 132, 331] to center a single button.
The Chooser uses the List Manager to produce and display the standard device list. You can supply a list definition procedure to modify this list. For example, you might want to include pictures or icons in your list. To do this, you must provide a resource of type 'LDEF' with an ID of –4096. See the List Manager chapter in the Inside Macintosh: Macintosh Toolbox volume for the mechanics of list construction and for the list data structure.
Creating a Device Package
Like a driver resource, a device package has two parts:
n the package header that contains some flags and some other data about the driver
n the package code that responds to Chooser messages
The package header contains a few words of flags and other data and a branch instruction to the package’s Device routine. Figure 2-3 shows the format of a package header.
The format of a package header
Since the Chooser expects the Package routine to be at the beginning of the device package, the first field of the package header should be a BRA.S instruction to the package code.
The device ID is an integer that identifies the device. The version word differentiates versions of the driver code.
The package code should implement the Package function. The section “How the Chooser Works” on page 2-21 gives the interface for this function. The next section, “Responding to the Chooser,” discusses how to implement this function.
The flags field contains information about the device and the device package. Table 2-2 lists the meaning of each bit of the flag word.
Device package flags
Bit Meaning
31 Set if an AppleTalk device
30–29 Reserved (clear to 0)
28 Set if the device package can have multiple instances selected at once
27 Set if the device package uses the Left button
26 Set if the device package uses the Right button
25 Set if no zone name has been saved
24 Set if device package uses actual zone names
23–21 Reserved (clear to 0)
20 Set if device uses the On and Off radio buttons and radio button label
19–17 Reserved (clear to 0)
17 Set if device package accepts the init message
16 Set if device package accepts the newSel message
15 Set if device package accepts the fillList message
14 Set if device package accepts the getSel message
13 Set if device package accepts the select message
12 Set if device package accepts the deselect message
11 Set if device package accepts the terminate message
10–0 Reserved (clear to 0)
Responding to the Chooser
This section gives more details about how your device package should respond when it receives a message from the Chooser.
When the Chooser sends your device package a message, the Chooser extension file is the current resource file, the Chooser window is the current grafPort, and the System Folder of the current startup disk is the default volume. Your device package must preserve all of these.
Table 2-3 lists the Chooser messages and how your package should respond to them.
Chooser messages and their meanings
Message Meaning
init The Chooser sends this message to your package when the user selects the icon representing your package in the icon list. The objName parameter contains a pointer to a data structure that contains a size word followed by four handles to ControlRecord data structures. The size is at least 18 bytes (2 bytes for the size word and 4 bytes for each of the handles). The handles reference the Left and Right buttons and the On and Off radio buttons in that order. Your package can respond to this message by setting up the initial button configuration. To display one of the radio buttons, you must call the ShowControl routine for it. To highlight them, you must call the SetCtlValue routine. See the chapter “The Control Manager” for more information. The p2 parameter is not used.
newSel If your device package allows multiple selections, the Chooser sends this message to your packages when the user changes or adds a selection. The objName and p2 parameters are not used.
fillList The Chooser sends this message when the user selects a device icon. The p1 parameter contains a handle to a List Manager list. Your device package should use the List Manager to fill this list with choices for the particular device. The objName and p2 parameters are not used.
getSel The Chooser sends this message to determine which entries should be selected. The p1 parameter contains a handle to a List Manager list. Your device package should respond by inspecting the list and setting the selected or unselected state of each entry, using the LSetSelect function. You should alter only the entries that require updating. The Chooser does not send this message for serial printers.
select If your device package does not allow multiple selections, the Chooser sends this message to your packages when the user selects a device from the device list. You should record the user’s selection, preferably in your Chooser extension file. Your device package may not call the List Manager in response to this message.
If your package accepts fillList messages, the objName parameter is undefined and the p2 parameter gives the row number of the selected device.
If your package does not accept fillList messages, the objName parameter contains a pointer to a string of up to 32 characters that contains the name of the device. If the device is an Appletalk device, the p2 parameter gives the AddrBlock value for the address of the selected AppleTalk device. (See the chapter “AppleTalk” for more information.)
deselect If your device package does not allow multiple selections, the Chooser sends this message to your packages when the user deselects a device in the device list. Your device package may not call the List Manager in response to this message.
If your package accepts fillList messages, the objName parameter is undefined and the p2 parameter gives the row number of the selected device.
If your package does not accept fillList messages, the objName parameter contains a pointer to a string (of up to 32 characters) containing the name of the device. If the device is an Appletalk device, the p2 parameter gives the AddrBlock value for the address of the selected AppleTalk device. (See the chapter “AppleTalk” for more information.)
terminate The Chooser sends this message when the user selects a different device icon, closes the Chooser window, or changes zones. Your device package should perform any necessary clean-up tasks, but should not dispose of the device list. The objName and p2 parameters are not used.
button The Chooser sends this message when the user clicks one of the buttons in the Chooser window. The low-order byte of the p2 parameter contains 1 if the user clicked the Left button, 2 if the user clicked the Right button, 3 if the user clicked the On radio button, and 4 if the user clicked the Off radio button. You must perform the appropriate highlighting for the radio buttons. The high-order word of this parameter contains the modifier bits from the mouse up event. See the chapters “The Control Manager” and “The Event Manager” for more information.
Allocating private storage
Device packages initially have no data space allocated. There are two ways your package can acquire data space:
n Use the List Manager to allocate extra memory in the device list.
n Create a resource.
The Chooser uses column 0 of the device list to store the names displayed in the list box; for device packages that do not accept fillList messages, it uses column 1 to store the 4-byte AppleTalk internet addresses of the devices in the list. Therefore, your device package can use column 1 and higher (if it accepts fillList messages) or column 2 and higher to store private data. You can use standard List Manager routines to add these columns, store data in them, and retrieve the data stored there. Your device package can also use the refCon field of the device list for its own purposes.
Using the device list is limited by the fact that the Chooser disposes of the device list whenever the user changes device types or changes the current zone. However, the Chooser does call your device package with the terminate message before it disposes of the list.
Also, if your device package does not accept fillList messages, the Chooser disposes of the device list whenever a new response from the AppleTalk zone interrogation arrives. However, the Chooser does call the getSel message immediately afterwards.
The second way to get storage space is to create a resource in the device resource file. This file is always the current resource file when the Chooser sends a message to the package, so you can use the GetResource routine to obtain a handle to the storage.
It is important for most device packages to record which devices the user has chosen. To do this, the recommended method is to create a resource in your driver resource file. This resource can be of any type; in fact, it’s advantageous to provide your own resource type so that no other program will try to modify it. If you choose to use a standard resource type, you should use only resource IDs in the range –4080 through –4065.
Writing a Desk Accessory
Desk accessories are device drivers that act like small applications. Desk accessories typically provide a user interface with a window and a menu, perform some limited function, and are opened from the Apple menu. The Chooser is an example of a desk accessory.
Desk accessories were originally designed for the Macintosh environment because they offered two distinct advantages over applications. They offered both a limited degree of multitasking and a primitive form of interprocess communication. However, applications under the system software version 7.0 environment enjoy far more sophisticated versions of these capabilities. Users can even open applications from the Apple menu. For these reasons, you would be better served by writing a small application than by writing a desk accessory.
If you’re certain you need to write a desk accessory, you should read this section. You might want to read the chapters about the Event Manager, the Window Manager, the Dialog Manager, and the Menu Manager in the Inside Macintosh: Macintosh Toolbox volume as well.
How Desk Accessories Work
In system software version 7.0 environment, when the user opens a desk accessory (or when an application calls the Menu Manager routine OpenDeskAcc) the system switches out the current application, loads the desk accessory into the system heap, and calls the desk accessory’s Open driver subroutine. The desk accessory can respond by creating its window and menu.
When events occur, the Event Manager directs them to the desk accessory by calling its Control driver subroutine. The Event Manager handles switching between applications and the desk accessories in the system heap and passing along the events meant for the desk accessory. The desk accessory need only respond to the Control request.
When the user closes the desk accessory (by closing its window or choosing Quit from its menu) or an application closes the desk accessory (by calling the Menu Manager routine CloseDeskAcc), the desk accessory disposes of its window (if any) and any other data structures associated with it.
In a single-application environment in system software version 6.0 or in multiple-application environment where the desk accessory is opened in the application’s partition (for example, when the user holds the Option key while opening the desk accessory from the Apple menu), the Event Manager handles events for desk accessories in a slightly different manner, although it still translates them into Control requests. See the chapter “The Event Manager” in the Inside Macintosh: Macintosh Toolbox volume for details.
Creating a Driver Resource for a Desk Accessory
To create a desk accessory, you must create it as a driver resource and include it in a resource file, as described in “Creating a Driver Resource” on page 2-5. Typically, you store your desk accessory driver resource in a desk accessory file, which allows you to include a 'BNDL' resource to give your desk accessory an icon, and the user stores your desk accessory file in the Apple Menu Items folder.
In addition to the driver header fields important for all drivers, described in “Creating a Driver Resource,” three fields are of particular importance to desk accessories:
n The drvrEMask field. This field contains an event masks specifying which events your desk accessory can handle. If your desk accessory has a window, you should include keyboard, activate, update, and mouse-down events, but you should not include mouse-up events. When an event occurs, the Event Manager checks this field to determine whether the desk accessory can handle the type of event, and if so, calls the desk accessory’s Control subroutine. See the chapter “The Event Manager” in the Inside Macintosh: Macintosh Toolbox for more information about events and event masks.
n The drvrMenu field. This field contains the menu ID of your desk accessory’s menu, if it has one, or any one of its menus, if it has more than one. Otherwise, it contains 0. The menu ID for a desk accessory’s menu must be negative and must be distinct from the menu ID for other desk accessories.
n The drvrDelay field and the dNeedTime flag of the drvrFlags field. Desk accessories often need to perform predefined actions periodically. For example, a clock desk accessory may want to change the time it displays every second. If your desk accessory needs to perform a periodic action, set the dNeedTime flag and use the drvrDelay field to indicate how often the action should occur. The section “Creating a Driver Resource” describes these fields in more detail.
Of the five possible driver subroutines, your desk accessory needs to implement three: Open, Close, and Control. You can implement a Prime and Status subroutine if needed.
Opening and Closing a Desk Accessory
In your desk accessory’s Open subroutine, you should:
n Create the desk accessory’s window. You can do this with the Dialog Manager function GetNewDialog or NewDialog. You should specify that the window be invisible because the OpenDeskAcc routine will display it. You should set the windowKind field of the window’s windowRecord data structure to the desk accessory’s driver reference number, which you can find in the device control entry. You should also store a copy of the window pointer in the dCtlWindow field of the device control entry.
n Allocate private storage as you would for any device driver.
n Create a menu or menus if needed for your desk accessory. You are responsible for adding your menu to the menu bar. See the chapter “The Menu Manager” in the Inside Macintosh: Macintosh Toolbox volume for more details.
If your Open subroutine is unable to complete its tasks (by running out of memory, for example), you should open only the minimum data structures possible, modify the code so it doesn’t respond to events, and display an alert indicating failure.
As for all drivers, your Close subroutine should undo the actions taken by the Open subroutine, disposing of the window and private storage, clearing the window pointer in the device control entry, and removing the menu from the menu bar.
Responding to Events
When the Event Manager determines an event has occurred that your desk accessory should handle, it checks the drvrEMask field of the driver header and, if that field indicates your driver handles the event type, it passes the event to your driver by means of your Control subroutine.
The Event Manager passes one of nine values in the csCode field to indicate the action to take:
Constant name Value Meaning
accEvent 64 Handle a given event
accRun 65 Time for periodic action
accCursor 66 Change cursor shape if appropriate
accMenu 67 Handle a given menu item
accUndo 68 Handle the Undo command
accCut 70 Handle the Cut command
accCopy 71 Handle the Copy command
accPaste 72 Handle the Paste command
accClear 73 Handle the Clear command
Along with the accEvent message, the Event Manager sends a pointer to an event record in the csParam field. You can respond to the event in whatever way is appropriate. For example, when your desk accessory becomes active you might install its menu in the menu bar.
If your desk accessory’s window is a modeless dialog box and you are calling the Dialog Manager routine IsDialogEvent in response to the event, you should set the windowKind field of your window record to 2 before you call the IsDialogEvent routine. Setting this field to 2 allows the Dialog Manager to recognize and handle the event properly. You should restore the original value of the windowKind field before returning from your Control subroutine.
The accCursor message makes it possible to change the shape of the cursor when its inside your desk accessory window and your desk accessory window is active. Your control routine should check whether the mouse location is in your window; if it is, you should set the cursor to the whatever cursor you desire by calling the QuickDraw routine InitCursor.
If your desk accessory window is a dialog box, you should respond to the accCursor message by generating a null event (storing the event code for a null event in an event record) and passing it to the Dialog Manager routine DialogSelect. This enables the Dialog Manager to blink the caret in editText items.
When the Event Manager sends the accMenu message, it provides the menu ID followed by the menu item number in the csParam field. You should take the appropriate action and then call the Menu Manager routine HiliteMenu with a value of 0 for the menuID parameter to remove the highlighting from the menu bar.
You should respond to the last five messages—accUndo through accClear—by processing the corresponding editing command in the desk accessory window, if appropriate. The chapter “The Scrap Manager” contains information about cutting and pasting.
Your desk accessory routines should restore the current resource file and the current grafPort if it changes either one.
Reference
This section describes the Device Manager routines, including the routines that allow your application to communicate with existing device drivers and the routines that provide support for your own device driver. This sections ends with a description of the 'DRVR' resource type.
Data Structures
The device control entry
TYPE DCtlEntry =
RECORD
dCtlDriver: Ptr; {pointer or handle to driver }
dCtlFlags: Integer; {flags }
dCtlQHdr: QHdr; {driver request queue header }
dCtlPosition: LongInt; {byte position}
dCtlStorage: Handle; {handle private storage}
dCtlRefNum: Integer; {driver reference number }
dCtlCurTicks: LongInt; {used internally }
dCtlWindow: WindowPtr; {pointer to desk accessory window }
dCtlDelay: Integer; {ticks between periodic actions }
dCtlEMask: Integer; {event mask for desk accessories }
dCtlMenu: Integer; {menu ID for desk accessories }
END;
dCtlDriver Contains a pointer or a handle to the driver, as determined by bit 6 of the dCtlFlags field.
dCtlFlags Contains flags about the abilities and state of the driver. The low-order byte contains these flags:
Bit Meaning
5 Set if driver is open.
6 Set if driver is RAM-based, that is, if
the dCtlDriver field contains a handle.
7 Set if driver is currently executing.
The high-order byte contains flags copied from the drvrFlags word of the driver resource. See “Creating a Driver Resource” on page 2-5 for more information.
dCtlQHdr Contains the header of the driver request queue, which is a standard Operating System queue. See the chapter “Operating System Utilities” in the Inside Macintosh: Operating System volume for more information about the QHdr data type.
dCtlPostion Indicates the current source or destination position for reading or writing. This field is only used by drivers of block devices. The value in this field is the number of bytes beyond the physical beginning of the medium used by the device. For example, immediately after the Disk Driver reads the first block of data from a 3.5-inch disk, this field contains the value 512.
dCtlStorage Contains a handle to a driver’s private storage. A driver may allocate a relocatable block of memory and keep a handle to it in this field.
dCtlRefNum Contains the driver reference number.
dCtlCurTicks Used internally.
dCtlWindow Contains a pointer to the desk accessory window. See “Reference” on page 2-34 for more information.
dCtlDelay Indicates the number of ticks to wait between periodic actions.
dCtlEMask Contains the desk accessory event mask. See “Reference” on page 2-34 for more information.
dCtlMenu Contains the menu ID of a desk accessory’s menu, if any. See “Reference” on page 2-34 for more information.
The Extended Device Control Entry
The device control entry for drivers that serve slot devices needs to store some additional information: the slot number, the slot resource ID, the device base address, and the external device ID. The extended device control entry, defined by the AuxDCE data type in “The extended device control entry” on page 2-35, contains additional fields to store these values.
The extended device control entry
TYPE AuxDCE=
RECORD
dCtlDriver: Ptr; {pointer or handle to driver }
dCtlFlags: Integer; {flags }
dCtlQHdr: QHdr; {driver request queue header }
dCtlPosition: LongInt; {byte position}
dCtlStorage: Handle; {handle private storage}
dCtlRefNum: Integer; {driver reference number }
dCtlCurTicks: LongInt; {used internally }
dCtlWindow: WindowPtr; {pointer to desk accessory window }
dCtlDelay: Integer; {ticks between periodic actions }
dCtlEMask: Integer; {event mask for desk accessories }
dCtlMenu: Integer; {menu ID for desk accessories }
dCtlSlot: Integer; {slot }
dCtlSlotId Integer; {slot ID }
dCtlDevVBase: Integer; {base address of card for driver }
reserved: Integer; {reserved; should be 0 }
dCtlExtDev: Integer; {external device ID }
fillByte: Integer; {reserved }
END;
The first eleven fields of the extended device control entry are identical to the fields of the device control entry, described in the previous section.
dCtlSlot Contains the slot number.
dCtlSlotId Contains the RsrcDirID for the slot resource.
dCtlDevBase Points to the device base address.
dCtlExtDev Contains the external device ID.
The chapter “The Slot Manager” in this book contains detailed information about these values.
Routines
The Device Manager provides a number of routines that provide low-level support for your own driver subroutines.
The GetDCtlEntry function allows your driver to obtain a handle to its device control entry.
The IODone routine allows your driver to notify the Device Manager that a request is complete. The Fetch and Stash routines allow your driver move characters into and out of data buffers. You pass a pointer to the device control entry in the A1 register to each of these three routines. The Device Manager uses the device control entry to locate the active request. If no such request exists, these routines generates system error dsIOCoreErr.
In the interest of speed, you invoke the Fetch, Stash, and IODone routines with jump vectors, stored in the global variables JFetch, JStash, and JIODone, rather than macros. You use a jump vector by moving its address onto the stack. An example is:
MOVE.L JIODone,-(SP)
RTS
The Fetch and Stash routines do not return a result code; if an error occurs, the System Error Handler is invoked.
2GetDCtlEntry
You can use the GetDCtlEntry function to obtain a handle to the device control entry of a device driver.
FUNCTION GetDCtrlEntry (refNum: Integer) : DCtlHandle;
refNum Contains the reference number of the driver.
DESCRIPTION
The GetDCtlEntry function returns a handle to the device control entry of the device driver indicated by the refNum parameter.
2Fetch
You can use the Fetch routine to get the next character from the data buffer.
DESCRIPTION
The Fetch routine gets the next character from the data buffer pointed to by the ioBuffer field of the parameter block of the pending request. It increments the ioActCount field by 1. If the ioActCount field equals the ioReqCount field, this function sets bit 15 of register D0. After receiving the last byte request, the driver should call the IODone routine.
Registers on entry
A1 pointer to the device control entry
Registers on exit
D0 character fetched; bit 15 = 1 if it’s the last character in the buffer
Jump vector
JFetch
2 Stash
You can use the Stash routine to store the next character from the data buffer.
DESCRIPTION
The Stash routine places the character in register D0 into the data buffer pointed to by the ioBuffer field of the parameter block of the pending request and increments the ioActCount field by 1. If the ioActCount field equals the ioReqCount field, this function sets bit 15 of register D0. After stashing the last byte requested, the driver should call the IODone routine.
ASSEMBLY-LANGUAGE INFORMATION
Registers on entry
A1 pointer to DCE
D0 character to stash
Registers on exit
D0 bit 15 = 1 if it’s the last character in the buffer
Jump vector
JStash
2IODone
You can use the IODone routine to notify the Device Manager that the current request has been satisfied.
DESCRIPTION
The IODone routine removes the current request from the driver request queue, marks the driver inactive, unlocks the driver and its device control entry (if allowed by the dNeedLock bit of the dCtlFlags word), and executes the completion routine if any. Then the Device Manager begins the next request in the driver request queue.
The section “Responding to the Device Manager” on page 2-9 explains in detail when to use this routine.
ASSEMBLY-LANGUAGE INFORMATION
Registers on entry
A1 pointer to DCE
D0 result code
Jump vector
JIODone
SEE ALSO
For an example of the IODone routine, see “Responding to the Device Manager” on page 2-9.
Resources
This section describes the driver resource, which you should use to store your device drivers and desk accessories. Listing 2-12 shows the Rez format of the 'DRVR' resource type. The driver resource has type 'DRVR'.
'DRVR' resource format
type 'DRVR' {
boolean = 0;
boolean dontNeedLock, needLock; /* lock drvr in memory */
boolean dontNeedTime, needTime; /* for periodic action */
boolean dontNeedGoodbye, needGoodbye; /* call before heap reinit */
boolean noStatusEnable, statusEnable; /* responds to Status */
boolean noCtlEnable, ctlEnable; /* responds to Control */
boolean noWriteEnable, writeEnable; /* responds to Write */
boolean noReadEnable, readEnable; /* responds to Read */
byte = 0;
integer; /* driver delay */
unsigned hex integer; /* DA event mask */
integer; /* DA menu */
unsigned hex integer; /* offset to Open */
unsigned hex integer; /* offset to Prime */
unsigned hex integer; /* offset to Control*/
unsigned hex integer; /* offset to Status */
unsigned hex integer; /* offset to Close */
pstring; /* driver name */
hex string; /* driver code */
};
The driver resource begins with seven flags that specify certain characteristics of the driver.
You need to set the dNeedLock flag if your driver’s code should be locked in memory.
You set the dNeedTime flag of the drvrFlags word if your device driver needs to perform some action periodically.
You need to set the dNeedGoodbye flag if you want your application to receive a goodBye Control request before the heap is reinitialized.
The last four flags indicate which Device Manager requests the driver’s routines can respond to.
The next element of the resource specifies the time between periodic tasks.
The next two elements provide an event mask and menu ID for desk accessories. The section “Writing a Desk Accessory” on page 2-31 describes these fields.
Offsets to the driver routines follow the desk accessory fields. See the section “Entering and Exiting From Driver Subroutines” on page 2-9 for more information about the subroutine offsets.
The next element of the driver resource is the driver name. You can use uppercase and lowercase letters when naming your driver, but the first character should be a period—.YourDriver, for example.
Your driver routines, which follow the driver name, must be aligned on a word boundary.
The section “Creating a Driver Resource” on page 2-5 discusses this structure in detail.
Summary
This section lists the declarations and definitions from this chapter. You might want to supplement this information with the information in the summary section of the chapter “The Device Manager,” in this book.
Constants
{Chooser messages}
CONST initMsg = 11; {the user selected this device package}
newSelMsg = 12; {the user made new device selections}
fillListMsg = 13; {fill the device list with choices}
getSelMsg = 14; {mark one or more choices as selected}
selectMsg = 15; {the user made a selection}
deselectMsg = 16; {the user cancelled a selection}
terminateMsg = 17; {allows device package to clean up}
buttonMsg = 18; {the user selected a button}
Data Types
TYPE DCtlEntry =
RECORD
dCtlDriver: Ptr; { pointer or handle to driver }
dCtlFlags: Integer; { flags }
dCtlQHdr: QHdr; { driver request queue header }
dCtlPosition: LongInt; { byte position used by I/O operations }
dCtlStorage: Handle; { handle to driver’s private storage }
dCtlRefNum: Integer; { driver reference number }
dCtlCurTicks: LongInt; { used internally }
dCtlWindow: WindowPtr; { pointer top driver’s window }
dCtlDelay: Integer; { ticks between periodic actions }
dCtlMenu: Integer; {menu ID (for desk accessories) }
dCtlSlot: Integer; {slot }
dCtlSlotId Integer; {slot ID }
dCtlDevVBase: Integer; {base address of card for driver }
reserved: Integer; {reserved; should be 0 }
dCtlExtDev: Integer; {external device ID }
fillByte: Integer; {reserved }
END;
AuxDCEPtr = ^AuxDCE;
AuxDCEHandle = ^AuxDCEPtr;
Routines
Driver Support Routines
FUNCTION GetDCtrlEntry (refNum: Integer) : DCtlHandle;
Assembly-Language Information
Routines Requiring Jump Vectors
Routine Jump Vector
Fetch JFetch
Stash JStash
IODone JIODone
The Slot Manager
Introduction to Slots and Cards3-3
Address allocations3-5
Firmware3-7
The format block3-7
The sResource directory3-8
sResource Data Structures3-8
sResource Types and sResource Names3-10
The Board sResource3-11
About the Slot Manager3-12
Using the Slot Manager3-14
Enabling and Disabling NuBus Cards3-14
Deleting and Restoring sResource Data Structures3-15
Enabling and Disabling sResource Data Structures3-15
Searching for sResource Data Structures3-16
Getting Information from sResource Data Structures3-18
Reference to the Slot Manager3-20
Data Structures3-20
Determining the Version of the Slot Manager3-22
Finding sResource Data Structures3-24
Enabling, Disabling, Deleting, and Restoring sResource
Data Structures3-35
Getting Information from sResource Data Structures3-40
Loading Drivers and Executing Code from sResource
Data Structures3-53
Traversing Firmware of Expansion Cards3-57
Getting Information about Expansion Cards and
Declaration ROMs3-58
Getting Access to Expansion Cards’ Parameter RAM3-65
Summary of the Slot Manager3-69
3The Slot Manager
This chapter describes how your application or device driver can use the Slot Manager to identify expansion cards and communicate with the firmware on each card.
You need to use the Slot Manager only if you are writing an application or a device driver that must address an expansion card directly. For example, you need to use the Slot Manager if you are writing a driver for a video card, but not if you only want to display information on a monitor for which a device driver already exists.
The Slot Manager provides routines to help you search through the data structures that expansion cards use to organize the information in their firmware. The meaning of the information in the data structures varies from card to card; you need to know the specifics of a card in order to interpret its data structures.
This chapter provides a brief introduction to Apple’s implementation of NuBus and expansion cards. This introduction explains the firmware data structures, but does not give much detail about the information these data structures contain. To interpret these data structures, you need to know the information in Designing Cards and Drivers for the Macintosh Family, as well as information specific to the card you’re examining.
If you are designing an expansion card, you must read Designing Cards and Drivers for the Macintosh Family. If you are writing a driver for a device on a card, you should also read the chapters “The Device Manager” and “Writing Your Own Device Driver” in this book.
After the introduction to the NuBus and expansion card design, this chapter discusses how you can
n determine the version of the Slot Manager that is available
n find information in the firmware of an expansion card
n enable and disable data structures in expansion card firmware
n load drivers and execute code from expansion card firmware
n check the status of expansion cards
n read and change the slot parameter RAM
Introduction to Slots and Cards
The Macintosh family of computers supports a processor-direct slot (PDS) expansion interface and a NuBus expansion interface.
The processor-direct slot, or PDS, is connected to the processor bus, which gives it direct access to the microprocessor and therefore a speed advantage over the NuBus. However, since the PDS expansion interface is an extension of the microprocessor, the configuration of the slot connector depends on which processor is used by the computer. For information specific to PDS expansion cards, see Designing Cards and Drivers for the Macintosh Family.
The Macintosh II-family computers include the NuBus expansion interface with configurations ranging from one to six identical slot connectors. The Macintosh system uses a slot ID in the range $09 to $0E to identify NuBus slots.
For convenience, this section refers to a NuBus configuration with six slots represented by slot IDs $09 through $0E. Keep in mind that some Macintosh family computers use only some of these slot IDs. For example, the Macintosh IIcx uses only slot IDs $09 through $0B. See Guide to the Macintosh Family Hardware for more information. <36pt\>\x12 <8bat\>u
In the Macintosh II-family of computers, the processor bus (which connects the CPU to RAM, ROM, the FPU) and the NuBus (which connects the NuBus expansion slots) are connected by a processor-bus to NuBus interface, or bus interface, as shown in Figure 3-1.
Simplified processor-bus and NuBus architecture
Both the processor bus and the NuBus are 4 bytes wide. The bus interface transfers bytes between the buses in byte lanes. Because the processor bus and the NuBus interpret the significance of bytes within words differently, the bus interface must perform byte lane swapping between the two buses.
The bus interface also performs some address translation between the two buses. It maps certain address ranges on each bus are to different address ranges on the other bus.
Designing Cards and Drivers for the Macintosh Family discusses byte lanes and address translation in more detail.
The next section, “Address allocations,” discusses the address ranges assigned by the Macintosh architecture to each NuBus slot.
The section “Firmware,” on page 3-7 introduces the data structures card’s use to organize information in their firmware.
Address allocations
The Macintosh architecture assigns certain address ranges to each NuBus slot. The processor communicates with an expansion card by reading and writing to memory in the address range of the card. The expansion cards can also communicate with each other in this manner.
The NuBus architecture allows full 32-bit addresses, providing four gigabytes of address space. Suitably equipped Macintosh computers with system software version 7.0 also support full 32-bit addressing, while earlier versions of the system software use 24-bit addressing. This section describes address space allocation in both the 24-bit and 32-bit modes.
In 32-bit mode, the Macintosh architecture assigns two address ranges to each NuBus slot: a 256-megabyte super slot space and a 16-megabyte standard slot space.
The four gigabytes of the 32-bit address space contain 16 regions of 256 megabytes apiece, each of which constitutes the super slot space for one possible slot ID. Each super slot space spans an address range $s000 0000 through $sFFF FFFF, where s is a hexadecimal digit $0 through $F.
For example, the address ranges $9000 0000 – $9FFF FFFF constitute the super slot space for slot ID $09. Since the Macintosh II family uses only slot IDs $09 through $0E, only six super slot spaces, $9xxx xxxx through $Exxx xxxx, are actually used.
The standard slot spaces are 16 megabytes apiece and have addresses of the form $Fsxx xxxx (that is, $Fs00 0000 – $FsFF FFFF) where s is the slot ID. As with super slot spaces, the Macintosh uses only standard slot spaces corresponding to slot IDs $9 through $E (that is $F9xx xxxx through $FExx xxxx).
Figure 3-2 shows the super slot and standard slot subdivisions of the 32-bit addressing region.
NuBus address space
In Figure 3-2, the super slot space and the standard slot space for slot ID $09 are shaded.
In 24-bit mode, the Macintosh system can only address a fraction of each card’s allocated address range. In this mode, the system assigns each slot a one megabyte minor slot space. The bus interface translates 24-bit address on the processor bus with the form $sx xxxx (where s is a slot ID) into 32-bit NuBus addresses of the form $Fs0x xxxx, which is the first megabyte of the slot’s standard slot space.
For example, 24-bit addresses in the range $90 0000 through $9F FFFF constitute the minor slot space corresponding to slot ID $09. The hardware translates these addresses into the NuBus address range $F900 0000 through $F90F FFFF.
Table 3-1 shows the address allocations for each slot ID.
Slot allocations by slot ID
Slot 24-bit minor 32-bit minor Standard Super
ID slot space slot space slot space slot space
$9 $9x xxxx $F90x xxxx $F9xx xxxx $9xxx xxxx
$A $Ax xxxx $FA0x xxxx $FAxx xxxx $Axxx xxxx
$B $Bx xxxx $FB0x xxxx $FBxx xxxx $Bxxx xxxx
$C $Cx xxxx $FC0x xxxx $FCxx xxxx $Cxxx xxxx
$D $Dx xxxx $FD0x xxxx $FDxx xxxx $Dxxx xxxx
$E $Ex xxxx $FE0x xxxx $FExx xxxx $Exxx xxxx
Firmware
The firmware of a NuBus expansion card contains information that identifies the card and its functions, and allows the card to communicate with the Slot Manager. It may also include other information, such as initialization code or code for drivers that communicate with devices on the card. The sole purpose of many Slot Manager routines is to provide access to this firmware information.
This section discusses the structure of the information in firmware. You’ll need to understand the structure of this information in order to use the Slot Manager routines. If you plan to design firmware, you will want to supplement the information in this section with the information in Designing Cards and Drivers for the Macintosh Family.
The physical location of an expansion card’s firmware is called its declaration ROM. The firmware in a card’s declaration ROM includes these elements:
n The format block. The format block allows the Slot Manager to find the declaration ROM and to validate it. It contains some identification information and an offset to the sResource directory.
n The sResource directory. The sResource directory is a list that contains offsets to all of the sResource data structures in the card’s firmware.
n The sResource data structures. A typical sResource data structure contains information about a single function or capability of the expansion card, although some sResource data structures may contain other data—for example, device drivers, icons, fonts, code, or vendor-specific information.
Every card must have a single Board sResource data structure, which contains information about the card as a whole. An sResource data structure relating to a specific function is called a functional sResource, and a card may have as many of them as necessary.
The next few sections discusses these data structures in more detail.
The format block
The format block always resides at the highest address in the standard slot space of a declaration ROM. It contains information about the declaration ROM and an offset to the sResource directory. The Slot Manager uses the format block to validate the declaration ROM and locate the sResource data structures.
The format block also contains a value that specifies which of the four byte lanes the declaration ROM uses, called the valid byte lanes. Some declaration ROMs do not use all four byte lanes and therefore the system cannot address every memory location in their address space.
Designing Cards and Drivers for the Macintosh Family defines the structure of the format block and gives examples of how the valid byte lanes affect communication with a declaration ROM.
The sResource directory
The sResource directory lists all the sResource data structures in the card firmware and provides an offset to each one.
Figure 3-3 shows the format of the sResource directory.
The sResource directory structure
Each element of the sResource directory contains an 8-bit sResource ID followed by a 24-bit offset. The sResource ID identifies the sResource data structure. Each sResource data structure in the card firmware has a unique ID. Apple reserves ID numbers in the range $00 – $EF for sResource data structures defined for all declaration ROMs; at present there is only one of these: sResource ID $01, which indicates a Board sResource. The sResource ID numbers appear in the sResource directory in ascending order.
The sResource ID $FF indicates an end-of-list marker.
The offset field of each entry contains an offset (counting only bytes accessible by valid byte lanes) to the sResource data structure. The final offset field must have a value of 0.
sResource Data Structures
Every sResource data structure contains of an sResource list. Each entry of an sResource list contains information about the sResource or contains a signed offset to another data structure, which might contain icons, code, device drivers, or other information relating to the sResource.
The sResource data structure is made up of its sResource list and all the information pointed to by the sResource list entries.
An sResource data structure is sometimes referred to as a slot resource. Note, however, that a slot resource is a data structure in the firmware of a NuBus card and not a type of Macintosh resource. <36pt\>\x12 <8bat\>u
The sResource list structure is similar to the sResource directory structure. Each sResource list entry has an 8-bit ID field, which identifies the type of the entry, and a 24-bit offset field, which can contain a byte of data, a word of data, or an offset to a larger block of data. The final entry in an sResource must contain the value $FF in the type field and the value 0 in the data field.
Figure 3-3 shows the format of an sResource list.
sResource list structure
The ID field of each entry indicates the type of information in the offset field of the entry. Apple reserves the range $00 – $7E for common sResource entry types. Designing Cards and Drivers for the Macintosh Family gives a complete list of the Apple-defined sResource list IDs and their meanings.
The offset field of each entry in an sResource list can contain data or it can contain an offset to a block of data. This field takes one of three possible forms:
n It may contain two $00 bytes followed by an 8-bit byte of data.
n It may contain a $00 byte followed by a 16-bit word of data.
n It may contain a 24-bit offset to larger data structures
Table 3-2 lists the larger data structures commonly used in sResource data structures.
Larger Data Types
Data Type Description
Long 32 bits, signed or unsigned.
Pointer 32 bits, signed or unsigned.
cString One-dimensional array of bytes, ending with $00.
sBlock A sized block of data. See Figure 3-5.
SExecBlock A sized block of code. See Figure 3-5.
The sBlock and SExecBlock data structures begin with a size field, which contains the physical size of the block (including the size field). The sBlock structure follows the size field with its actual data. The SExecBlock field follows the size field with some additional fields and code.
Format of the sBlock and SExecBlock data structures
sResource Types and sResource Names
The Slot Manager requires that each sResource list contain an sRsrcType entry, which identifies the sResource type, and an sRsrcName entry, which provides the sResource name.
The sRsrcType entry contains an offset to an sRsrcType record—a record containing 2 long words that identifies the sResource type.
Figure 3-6 shows the format of an sRrscType record.
The sRsrcType record format
Category The most general classification of card functions. Some example categories are CatDisplay and CatNetwork.
cType The subclass of the category. Within the CatDisplay category, for example, there is a TypVideo subcategory; within the CatNetwork category, there is a TypAppleTalk subcategory.
DrSW The driver software interface of the subcategory. For example, under the category Display and the subcategory Video, there is a DrSWApple software interface, which indicates the Apple-defined interface to work with QuickDraw using Macintosh Operating System frame buffers.
DrHW The identification of the specific hardware device associated with the driver software interface.
The value of each sRsrcType entry is unique and assigned by Apple Developer Technical support.
The sRsrcName entry contains an offset to a cString data structure containing the sResource name. By convention, the sRsrcName field is derived by stripping the prefixes from the sRsrcType values and separating the fields by underscores. For example, the sRsrcName for an sResource whose sRsrcType values are CatDisplay, TypVideo, DrSWApple, and DrHWTFB becomes 'Display_Video_Apple_TFB'.
Designing Cards and Drivers for the Macintosh Family provides information about these and other entry types.
The Board sResource
The Board sResource is a special sResource data structure that must be present in the firmware of every card that communicates with the Slot Manager. By convention, it has sResource ID $01 and it appears first in the sResource directory.
The entries in the Board sResource list provide the Slot Manager with a card’s identification number, vendor information, board flags, and initialization code. Like all sResource data structures, the Board sResource list must include an sRsrcType entry and an sRsrcName entry. The Board sRsrcType entry must contain the constants CatBoard ($0001), TypBoard ($0000), DrSWBoard ($0000), and DrHWBoard ($0000). The Board sResource name does not follow the same convention as other sResource names—the Board sResource name is the name of the entire card, for example 'Mac II Monochrome Video Card'.
The Board sResource list must also contain a BoardId entry, a word that contains the card design identification number assigned by Apple Macintosh Developer Technical Support. Other Apple-defined entries specifically for Board sResource data structures are discussed in Designing Cards and Drivers for the Macintosh Family.
Figure 3-7 shows a sample Board sResource. It shows an sRsrcType and sRsrcName entry and also includes three entry types, BoardID, PRAMInitData, and PrimaryInit, that are discussed in Designing Cards and Drivers for the Macintosh Family.
Sample Board sResource
About the Slot Manager
The Slot Manager provides three basic services:
n At system startup time, it examines each slot and initializes any expansion cards it finds.
n It maintains data structures that contain information about each slot and every available sResource data structure.
n It provides functions that allow you to get information about expansion cards and their sResource data structures.
There are two variations of the system software version 7.0 Slot Manager: version 1 and version 2. Version 1 of Slot Manager is supplied with the version 7.0 System file on disk for use with Macintosh II-family computers designed and built before the system software version 7.0 was available. Version 2 is included in the ROM of newer Macintosh II-family computers.
When the user starts up a Macintosh II-family computer, the version of the Slot Manager in ROM searches each slot for a declaration ROM and creates a slot information record for each slot. The section “Getting Information about Expansion Cards and Declaration ROMs” reference section, later in this chapter, shows the definition of the SInfoRecord data type.
The ROM of early Macintosh II-family computers may contain early versions of the Slot Manager that address NuBus cards in 24-bit mode and may not be able to identify some cards. After version 1 of the Slot Manager is loaded, it locates these cards. <36pt\>\x12 <8bat\>u
As the Slot Manager searches the slots, it identifies all of the sResource data structures in each declaration ROM and creates a table—the Slot Resource Table, or SRT—that lists all of the sResource data structures currently available to the system.
The Slot Manager then initializes the six parameter RAM bytes reserved for each slot. If the slot has an expansion card with a PRAMInitData entry in its Board sResource, the Slot Manager uses the values in that entry to initialize the parameter RAM; otherwise, it clears the parameter RAM bytes to 0.
After the parameter RAM initialization, the Slot Manager disables interrupts and executes the code in the PrimaryInit entry of each card’s Board sResource.
If certain values (defined by the Start Manager) are set in a card’s parameter RAM, a card with an sRsrcBootRec entry may take over the system startup process. The system gives control to the code in this entry early in the startup sequence, before the system patches are loaded.
Designing Cards and Drivers for the Macintosh Family describes the PRAMInitData, PrimaryInit, and sRsrcBootRec entry types.
If no card takes over, the normal system startup continues. After version 1 of the Slot Manager is loaded, it conducts a second search for declaration ROMs, this time in 32-bit mode. If it finds any new NuBus cards, it adds their sResource data structures to the Slot Resource Table and executes the code in their PrimaryInit entries. (Version 2 of the Slot Manager, which resides in ROM, does not need to conduct a second search.)
After all system patches have been installed, version 1 or later of the Slot Manager executes the code in any SecondaryInit entries it finds in the declaration ROMs. It does not reexecute the code from PrimaryInit entries, reinitialize parameter RAM, or restore any sResource data structures deleted by the PrimaryInit code.
Versions of the Slot Manager earlier than version 1 do not execute code from SecondaryInit entries. <36pt\>\x12 <8bat\>u
After the Slot Manager executes SecondaryInit code, it searches for sResource data structures that have an sRsrcFlags entry with the fOpenAtStart flag set. When the Slot Manager finds an sResource data structure with this flag set, it loads the device driver from the sRsrcDrvrDir entry of the sResource, or calls the code in sResource’s sRsrcLoadRec entry, which loads the sResource’s device driver.
Finally, the system executes initialization ('INIT') resources.
See Designing Cards and Drivers for the Macintosh Family for details about the sRsrcFlags, sRsrcDrvrDir, and sRsrcLoadRec entry types.
Using the Slot Manager
The majority of Slot Manager routines search for sResource data structures in the Slot Resource Table or provide information from these structures.
The Slot Manager provides a variety of methods to find an sResource data structure. These methods include searching for an sResource data structure with a particular sResource ID, searching for an sResource data structure with a particular sResource type, searching through all sResource data structures, searching through only the enabled sResource data structures, and so on.
The Slot Manager also provides a number of routines that return information from sResource list entries. Some of these routines, like the SReadByte and SGetCString functions, return one particular type of data structure. Some others, like the SFindStruct function, can return information about any data structure. Still other functions, the SGetDriver and SExec functions, not only return information from an sResource data structure but also preform functions like loading the sResource data structure’s driver or executing the code of an SExecBlock data structure.
In addition to searching for sResource data structures and getting information out of them, the Slot Manager allows you to enable and disable NuBus cards, manipulate the Slot Resource Table, get information from slot information records, get status information, and read and change expansion cards’ parameter RAM.
Enabling and Disabling NuBus Cards
Version 1 and later of the Slot Manager allows you to temporarily disable your NuBus card. You might want to do this, for example, if you are designing a NuBus card that must be addressed in 32-bit mode or that requires RAM-based system software patches to be loaded into memory before the card is initialized. Your PrimaryInit code can disable the card temporarily and the SecondaryInit code can reenable it.
To disable a NuBus card temporarily, the initialization routine in your PrimaryInit record should return in the seStatus field of the SEBlock data structure (described in “Loading Drivers and Executing Code from sResource Data Structures” on page 3-53) an error code with a value in the range svTempDisable ($8000) through svDisabled ($8080). The Slot Manager places this code in the siInitStatusV field of the slot information record that the Slot Manager maintains for the slot, and it places the fatal error smInitStatVErr (–355) in the initStatusA field of the sInfo record. The card and its sResource data structures are then unavailable for use by the Operating System.
After the Operating System loads RAM patches, the Slot Manager checks the value of the siInitStatusA field. If this value is greater than or equal to 0, indicating no error, the Slot Manager executes the SecondaryInit code. If the value in the siInitStatusA field is smInitStatErr, the Slot Manager checks the siInitStatusV field. If the value of the siInitStatusV field is in the range svTempDisable through svDisabled, the Slot Manager clears the siInitStatusA field and runs the SecondaryInit code.
For examples of PrimaryInit and SecondaryInit code, see Designing Cards and Drivers for the Macintosh Family.
Deleting and Restoring sResource Data Structures
Some NuBus cards have sResource data structures to support a variety of combinations of system configurations or modes. The Slot Manager loads all of the sResource data structures during system initialization, and then the card’s PrimaryInit code can delete from the Slot Resource Table any sResource data structures that are not appropriate for the system as configured. If the user changes the system configuration or selects a different mode of operation, you can reinstall a deleted sResource data structure. The SDeleteSRTRec function deletes sResource data structures; the InsertSRTRec function reinstalls them.
Because none of the Slot Manager functions can search for sResource data structures that have been deleted from the Slot Resource Table, you must keep a record of all sResource data structures that you have deleted so that you will have the appropriate parameter values when you want to reinstall one.
When you reinstall an sResource data structure, you can also update the dCtlDevBase field in the corresponding device driver’s device control entry. The dCtlDevBase field holds the address of the sResource data structure that is used by that device driver. For a video driver, for example, the dCtlDevBase field might contain the address of the frame buffer. Use the InsertSRTRec to update the dCtlDevBase field. See the Device Manager chapter of this volume for a definition of the device control entry.
Enabling and Disabling sResource Data Structures
Under certain circumstances, you might want to disable an sResource data structure while it remains listed in the Slot Resource Table. For example, a NuBus card might provide several modes of operation, only one of which can be active at a given time. Your application might want to disable the sResource data structures associated with all but the active mode, but still list all available modes in a menu. When the user selects a new mode, your application can then disable the currently active sResource data structure and enable the one the user selected.
You use the SetSRsrcState function to enable or disable an sResource data structure. Listing 3-1 disables the sResource data structure in slot $0A with an sResource ID of 128 and enables the sResource data structure in the same slot with an ID of 131.
Disabling and Enabling sResource data structures
VAR
mySpBlk: SpBlock;
myErr: OSErr;
BEGIN
{Set required values in parameter block.}
WITH mySpBlk DO
BEGIN
spParamData := 1; {disable}
spSlot := $0A; {slot number}
spID := 128; {sResource ID}
spExtDev := 0; {ID of external device}
END;
myErr := SetSRsrcState(@mySpBlk);
IF myErr <> noErr THEN DoError(myErr);
WITH mySpBlk DO
BEGIN
spParamData := 0; {enable}
spSlot := $0A; {slot number}
spID := 131; {sResource ID}
spExtDev := 0; {ID of external device}
END;
myErr := SetSRsrcState(@mySpBlk);
IF myErr <> noErr THEN DoError(myErr);
END;
Searching for sResource Data Structures
The Slot Manager provides several routines that search for sResource data structures in the Slot Resource Table. These routines all allow you to specify for which sResource data structures to search, but they each provide slightly different options.
The SFindSRsrcPtr and SGetSRsrcPtr functions are the simplest. The SFindSRsrcPtr function requires you to specify a slot number and an sResource ID and returns a pointer to the sResource data structure with the specified ID in the specified slot.
The SGetSRsrcPtr function allows you to specify that you want the function to return a pointer to the next sResource data structure—that is, the one with the next higher sResource ID. When searching for the next sResource data structure, the SGetSRsrcPtr function also allows you to search for both enabled and disabled sResource data structures, or enabled ones only.
The SNextSRsrc and SGetSRsrc functions are similar, although they return more information about the sResource data structure, such as the fields of its sRsrcType entry.
The SNextTypeSRsrc and SGetTypeSRsrc functions allow even more control. With these functions, you can search for the next sResource data structure with a particular sResource type.
Table 1-1 summarizes the Slot Manager search routines and the options available for each.
Slot Manager search routines
State of Which Type of
sResources it Slots it sResource it sResource it
Function searches for searches searches for searches for
SFindSRsrcPtr Enabled only Specified slot Specified Any type
sResource only
SNextSRsrc Enabled only Specified slot and Next sResource Any type
higher slots only
SGetSRsrcPtr*, Your choice of Your choice of one Your choice of Any type
SGetSRsrc* enabled only or slot only or specified
both enabled and specified slot and sResource or
disabled higher slots next sResource
SNextTypeSRsrc Enabled only Specified slot and Next sResource Specified type
higher slots only only
SGetTypeSRsrc* Your choice of Your choice of Next sResource Specified type
enabled only or one slot only or only only
both enabled and specified slot and
disabled higher slots
* Available only with system software version 7.0 Slot Manager
Listing 3-2 shows how to search all slots for both enabled and disabled sResource data structures with an sResource type category of catDisplay and an sResource type subcategory of typVideo.
Searching for sResource data structures
VAR
mySpBlk: SpBlock;
myErr: OSErr;
BEGIN
{Set required values in parameter block.}
WITH mySpBlk DO
BEGIN
spParamData := 1; {fAll flag 1: search all sResources}
{fOneSlot flag 0: search all slots}
spCategory := $catDisplay; {search for Category catDisplay}
spCategory := $typeVideo; {search for cType typeVideo}
spDrvrSW := 0; {this field not being matched}
spDrvrHW := 0; {this field not being matched}
spTBMask := 0; {match only Category and cType fields}
spSlot := 0; {start search from here}
spID := 0; {start search from here}
spExtDev := 0; {ID of the external device}
END;
myErr := 0;
WHILE myErr = noErr DO {loop to search sResources}
BEGIN
myErr := SGetTypeSRsrc(@mySpBlk);
MySRsrcProcessor(mySpBlk); {your routine to process results}
END;
IF myErr <> smNoMoresRsrcs THEN DoError(myErr);
END;
Getting Information from sResource Data Structures
If you are writing a driver for a card device, you will most likely want access to the information in an sResource data structure.
The Slot Manager provides many functions that return information from the entries of an sResource data structure. The SOffsetData, SReadByte, and SReadWord functions return information from the offset field of an sResource list entry. The SReadLong, SGetCString, and SGetBlock functions return copies of the standard data structures pointed to by the offset field of an sResource list entry. The SFindStruct and SReadStruct functions allow access to other data structures pointed to by sResource list entries.
Listing 3-3 shows an example of reading information from an sResource. This example examines the name of the card in slot $0A.
Searching for sResource data structures
VAR
mySpBlk: SpBlock;
myErr: OSErr;
BEGIN
{First get a pointer to the Board sResource for slot $0A.}
WITH mySpBlk DO
BEGIN
spSlot := $0A; {search in this slot}
spID := 1; {assume Board sResource has ID 1}
END;
myErr := SFindSRsrcPtr(@mySpBlk);
IF myErr <> noErr THEN DoError(myErr);
{The spsPointer field of mySpBlock now contains a pointer }
{ to the Board sResource list. The SGetCString function }
{ uses this field as one of two input fields.}
mySpBlk.spID := 2; {sRsrcName entry}
myErr := SGetCString(@mySpBlk);
IF myErr <> noErr THEN DoError(myErr);
{The spResult field now points to a copy of the cString}
MyProcessCardName(mySpBlk.spResult);
END;
Reference to the Slot Manager
This section describes the routines you use to get information about the Slot Manager, expansion cards, and sResource data structures. Most of the routines in this section find an sResource data structure given some information about it or read information from an entry in an sResource list. Some of the routines allow you to read and set information about expansion cards, such as their parameter RAM values, and others allow you to manipulate Slot Manager data structures, like the Slot Resource Table.
Data Structures
Every Slot Manager function requires a pointer to a parameter block as a parameter, and returns an OSErr result code. Each routine uses only a subset of the fields of the parameter block, which is defined in Listing 3-4. See the routine descriptions for a list of the fields used with each routine.
Slot Manager parameter block
TYPE SpBlockPtr = ^SpBlock;
SpBlock = PACKED RECORD
spResult: LongInt; {result}
spsPointer: Ptr; {structure pointer}
spSize: LongInt; {size of structure}
spOffsetData: LongInt; {offset or data}
spIOFileName: Ptr; {reserved for Slot Manager}
spsExecPBlk: LongInt; {pointer to SEBlock data structure}
spParamData: Ptr; {flags}
spMisc: LongInt; {reserved for Slot Manager}
spReserved: LongInt; {reserved for Slot Manager}
spIOReserved: INTEGER; {ioReserved field from SRT}
spRefNum: INTEGER; {driver reference number}
spCategory: INTEGER; {Category field of sResource type}
spCType: INTEGER; {cType field of sResource type}
spDrvrSW: INTEGER; {DrvrSW field of sResource type}
spDrvrHW: INTEGER; {DrvrHW field of sResource type}
spTBMask: SignedByte; {sResource type bit mask}
spSlot: SignedByte; {slot number}
spID: SignedByte; {sResource ID}
spExtDev: SignedByte; {external device ID}
spHwDev: SignedByte; {hardware device ID}
spByteLanes: SignedByte; {valid byte lanes}
spFlags: SignedByte; {flags used by Slot Manager}
spKey: SignedByte; {reserved for Slot Manager}
END;
spResult A general-purpose field used to contain the results returned by several different routines.
spsPointer A pointer to a data structure. The field can point to an sResource data structure, a data block, or a declaration ROM, depending on the routine being executed.
spSize The size of the data block.
spOffsetData The contents of the offset field of an sResource data structure entry. Some routines use this field for other offsets or data.
spIOFileName Reserved for use by the Slot Manager.
spsExecPBlk Pointer to an SEBlock data structure (an SExec parameter block).
spParamData On input, this long word contains flags that determine what sResource data structures the Slot Manager searches. Bit 0 (the fAll flag) indicates when set that disabled sResource data structures should be included. When set, bit 1 (the fOneslot flag) restricts the search to sResource data structures on a single card. Bit 2 (the fNext flag) indicates when set that the routine find the next sResource data structure. The rest of the fields must be cleared to 0.
On output, this field indicates whether the sResource data structure is enabled or disabled (if 0, the sResource data structure is enabled; if 1, it is disabled).
spMisc Reserved for use by the Slot Manager.
spReserved Reserved for future use.
spIOReserved The value of the ioReserved field from the sResource data structure’s entry in the Slot Resource Table.
spRefNum The driver reference number of the driver associated with an sResource data structure, if there is one.
spCategory The Category field of the sResource type.
spCType The cType field of the sResource type.
spDrvrSW The DrvrSW of the sResource type.
spDrvrHW The DrvrHW of the sResource type.
spTBMask A mask that determines which sRsrcType fields the Slot Manager examines when searching for sResource data structures.
spSlot The number of the slot with the NuBus card containing the requested, or returned, sResource data structure.
spID The sResource ID of the requested, or returned, sResource data structure.
spExtDev The external device identifier. This field allows you to distinguish between devices on a card.
spHwDev The hardware device identifier from the sRsrcHWDevID field of the sResource data structure.
spByteLanes The byte lanes used by a declaration ROM.
spFlags Flags typically used by the Slot Manager.
spKey Reserved for use by the Slot Manager.
Since each routine uses a subset of the parameter block fields, each routine reference section includes a list of pertinent fields and how they are used.
The arrows show whether you provide a value in the field, the routine returns a value in the field, or both. The <zapf\>5 symbol designates fields that may be affected by the execution of the routine. Any value you store in one of these fields may be lost. Also, the meaning of these fields upon completion of the routine is undefined; your application should not depend on these values.
Determining the Version of the Slot Manager
Unlike many of the system software managers, which provide you with version information through the Gestalt function, the Slot Manager provides its own function for determining version information.
3SVersion
You can use the SVersion function to determine which version of the Slot Manager is in use by the Operating System.
FUNCTION SVersion (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spResult LongInt The Slot Manager version number.
<symbol\>\xac spsPointer Ptr A pointer to additional information.
DESCRIPTION
The SVersion function returns the version number of the Slot Manager in the spResult field. Version number 1 corresponds to the RAM-based Slot Manager and version number 2 corresponds to the ROM-based Slot Manager. Versions of the Slot Manager before system software version 7.0 do not recognize the SVersion function and return a nonfatal error smSelOOBErr. The spsPointer field is reserved for future use as a pointer to additional information.
SPECIAL CONSIDERATIONS
The SVersion function does not move memory; your application may call this function at interrupt time.
The SVersion function is available only with version 1 or later of the Slot Manager. You can use the error code smSelOOBErr to determine if an earlier version of the Slot Manager is in use by the Operating System.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SVersion function is _SlotManager ($A06E). The routine selector is $0008.
Trap macro Selector
_SlotManager $0008
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0008
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smSelOOBErr –338 Selector out of bounds; function not
implemented
SEE ALSO
For more discussion on the different versions of the Slot Manager, see “About the Slot Manager” on page 3-12.
Finding sResource Data Structures
The routines in this section locate sResource data structures in the Slot Resource Table and return pointers to them and additional information about them. You can use some of the routines to step through the sResource data structures, and some of the routines allow you to find disabled as well as enabled sResource data structures.
3SFindSRsrcPtr
You can use the SFindSRsrcPtr function to find an sResource data structure given its slot number and sResource ID. This function ignores disabled sResource data structures.
FUNCTION SFindSRsrcPtr (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spsPointer Ptr A pointer to the sResource data structure.
<symbol\>\xae spSlot SignedByte The slot number of the requested sResource.
<symbol\>\xae spId SignedByte The sResource ID of the requested sResource.
<zapf\>5 spResult LongInt
SPECIAL CONSIDERATIONS
The SFindSRsrcPtr function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SFindSRsrcPtr function is _SlotManager ($A06E). The routine selector is $0030.
Trap macro Selector
_SlotManager $0030
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0030
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For information about the sResource data structure, see the section “Firmware” on page 3-7.
3SGetSRsrcPtr
You can use the SGetSRsrcPtr function to find an sResource data structure given its slot number and sResource ID. This function allows you to search disabled sResource data structures and allows you to step through sResource data structures.
FUNCTION SGetSRsrcPtr (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spsPointer Ptr A pointer to the sResource data structure.
<symbol\>\xae spParamData LongInt The fAll, fOneslot, fNext flags.
<symbol\>\xae spSlot SignedByte The slot number of the requested sResource.
<symbol\>\xae spID SignedByte The sResource ID of the requested sResource.
<symbol\>\xae spExtDev SignedByte The external device identifier.
DESCRIPTION
The SGetSRsrcPtr function allows you to find any sResource data structure from the Slot Resource Table, even disabled ones.
You specify an sResource data structure with the spSlot, spID, and spExtDev fields. You must also include flags in bits 0, 1, and 2 of the spParamData field as follows:
n Set the fAll flag (bit 0) to search both enabled and disabled sResource data structures. Clear this flag to search only enabled sResource data structures.
n Set the fOneslot flag (bit 1) to search only the specified slot. Clear this flag to search all slots.
n Set the fNext flag (bit 2) to return information about the sResource data structure with the next higher sResource ID than the specified sResource data structure (or the first one on the next card if the fAll flag is set). Clear this flag to return data about the specified sResource data structure.
The SGetSRsrcPtr function returns a pointer to the specified sResource data structure, or the one following it if you set the fNext flag.
The SGetSRsrcPtr function updates the values in the spSlot, spID, and spExtDev fields to correspond to the sResource data structure that it found. If you cleared the fNext flag, these fields retain the values you specified when calling the function.
SPECIAL CONSIDERATIONS
The SGetSRsrcPtr function does not move memory; your application may call this function at interrupt time.
This function is available with version 1 or later of the Slot Manager.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SGetSRsrcPtr function is _SlotManager ($A06E). The routine selector is $001D.
Trap macro Selector
_Slot Manager $001D
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $001D
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource data structure, see the section “Firmware” on page 3-7.
3SRsrcInfo
You can use the SRsrcInfo function to find an sResource data structure given its slot number and sResource ID. This function also provides additional information about the sResource data structure.
FUNCTION SRsrcInfo (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spsPointer Ptr A pointer to the sResource data structure.
<symbol\>\xac spIOReserved Integer Value of Slot Resource Table ioReserved field.
The SRsrcInfo function allows you to find an sResource data structure from the Slot Resource Table, and provides additional information such as the type of the sResource data structure.
You specify an sResource data structure with the spSlot, spID, and spExtDev fields.
The SRsrcInfo function returns a pointer to the sResource data structure in the spsPointer field and information about the sResource type in the spRefNum, spCType, spDrvrSW, spDrvrHW fields. The function returns other information about the sResource data structure in the spIOReserved, spRefNum, and spHwDev fields.
SPECIAL CONSIDERATIONS
The SRsrcInfo function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SRsrcInfo function is _SlotManager ($A06E). The routine selector is $0016.
Trap macro Selector
_SlotManager $0016
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0016
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource data structure and fields of the sResource type, see “Firmware” on page 3-7.
For more control in finding sResource data structures, you can use the SGetSRsrc function, described on page 3-30, and the SGetTypeSRsrc function, described on page 3-33.
3SNextSRsrc
You can use the SNextSRsrc function to step through the sResource data structures on a card or from one card to the next.
FUNCTION SNextSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spsPointer Ptr A pointer to the sResource data structure.
<symbol\>\xac spIOReserved Integer Value of Slot Resource Table ioReserved field.
<symbol\>\xac spRefNum Integer The driver reference number.
<symbol\>\xac spCategory Integer Category field of sRsrcType.
<symbol\>\xac spCType Integer CType field of sRsrcType.
<symbol\>\xac spDrvrSW Integer DrvrSW field of sRsrcType.
<symbol\>\xac spDrvrHW Integer DrvrHW field of sRsrcType.
The SNextSRsrc function is similar to the SRsrcInfo function, except the SNextSRsrc function returns information about the sResource data structure that follows the requested one—that is, the one with the next entry in the sResource directory or the first sResource on the next card. The SNextSRsrc function skips disable sResources.
You specify a particular sResource data structure with the spSlot, spID, and spExtDev fields. The SNextSRsrc function finds the next sResource data structure, returns a pointer to it in the spsPointer field, and updates the spSlot, spID, and spExtDev fields to correspond to the sResource data structure it found. If there are no more sResource data structures, the SNextSRsrc function returns with a nonfatal status error.
The SNextSRsrc function returns other information about the sResource data structure in the spIoReserved, spRefNum, spCategory, spCType, spDrvrSW, and spDrvrHW fields.
SPECIAL CONSIDERATIONS
The SNextSRsrc function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SNextSRsrc function is _SlotManager ($A06E). The routine selector is $0014.
Trap macro Selector
_SlotManager $0014
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0014
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource directory and the sResource data structure, see “Firmware” on page 3-7.
For more control in finding sResource data structures, you can use the SGetSRsrc function, described on page 3-30, and the SGetTypeSRsrc function, described on page 3-33.
3SGetSRsrc
You can use the SGetSRsrc function to find any sResource data structure, even one that has been disabled.
FUNCTION SGetSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spsPointer Ptr A pointer to the sResource data structure.
<symbol\>\xab spParamData LongInt On input: fAll, fOneslot, fNext flags.
The SGetSRsrc function allows you to specify whether the function should include disabled sResources, whether it should continue looking for sResources in higher-numbered slots, and whether it should return information about the specified sResource data structure or the one that follows it.
You specify an sResource data structure with the spSlot, spID, and spExtDev fields. You must also include flags in bits 0, 1, and 2 of the spParamData field as follows:
n Set the fAll flag (bit 0) to search both enabled and disabled sResource data structures. Clear this flag to search only enabled sResource data structures.
n Set the fOneslot flag (bit 1) to search only the specified slot. Clear this flag to search all slots.
n Set the fNext flag (bit 2) to return information about the sResource data structure with the next higher sResource ID than the specified sResource data structure (or the first one on the next card if the fAll flag is set). Clear this flag to return data about the specified sResource data structure.
The SGetSRsrc function returns values in the spSlot, spID, and spExtDev fields corresponding to the sResource data structure that it found. If you cleared the fNext flag, these fields retain the values you specified when calling the function. In addition, the function returns 0 in the spParamData field if the sResource data structure is enabled or 1 if it is disabled. If you cleared the fAll bit, the spParamData field always returns the value 0.
The SGetSRsrc function also returns a pointer to the sResource data structure in the spsPointer field and other information about the sResource data structure in the spRefNum, spCategory, spCType, spDrvrSW, spDrvrHW, and spHwDev fields.
SPECIAL CONSIDERATIONS
The SGetSRsrc function does not move memory; your application may call this function at interrupt time.
The SGetSRsrc function is available only with version 1 or later of the Slot Manager.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SGetSRsrc function is _SlotManager ($A06E). The routine selector is $000B.
Trap macro Selector
_SlotManager $000B
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $000B
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource data structure, see the section “Firmware” on page 3-7.
For more control in finding sResource data structures, you can use the SGetSRsrc function, described on page 3-30, and the SGetTypeSRsrc function, described on page 3-33.
3SNextTypeSRsrc
You can use the SNextTypeSRsrc function to step through sResource data structures of one type.
FUNCTION SNextTypeSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spsPointer Ptr A pointer to the sResource data structure.
<symbol\>\xac spIOReserved Integer Value of Slot Resource Table ioReserved field.
The SNextTypeSRsrc function allows you to find the next sResource data structure, as does the SNextSRsrc function, but the SNextTypeSRsrc function also allows you to specify the type of sResource data structures to find. The SNextTypeSRsrc function skips disable sResources.
You specify an sResource data structure with the spSlot, spID, and spExtDev fields, and you specify the type of the sResource with the spCategory, spCType, spDrvrSW, and spDrvrHW fields. You must also use the spTBMask to specify which of these D fields should not be included in the search:
n Set bit 0 to ignore the DrvrHW field.
n Set bit 1 to ignore the DrvrSW field.
n Set bit 2 to ignore the cType field.
n Set bit 3 to ignore the Category field.
The SNextTypeSRsrc function returns values in the spSlot, spID, and spExtDev fields corresponding to the sResource data structure that it found, and it returns 0 in the spParamData field in that sResource is enabled or 1 if it is disabled.
The SNextTypeSRsrc function also returns a pointer to the sResource data structure in the spsPointer field and other information about the sResource data structure in the spIOReserved, spRefNum, spCategory, spCType, spDrvrSW, and spDrvrHW fields.
SPECIAL CONSIDERATIONS
The SNextTypeSRsrc function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SNextTypeSRsrc function is _SlotManager ($A06E). The routine selector is $0015.
Trap macro Selector
_SlotManager $0015
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0015
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource data structure, see the section “Firmware” on page 3-7.
For information on enabling and disabling sResource data structures, see the “Enabling and Disabling sResource Data Structures” on page 3-15 and the description of the SetSRsrcState function on page 3-35.
3SGetTypeSRsrc
You can use the SGetTypeSRsrc function to step through sResource data structures of one type, including disabled ones.
FUNCTION SGetTypeSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
You specify values and receive return values in the Slot Manager parameter block.
<symbol\>\xac spsPointer Ptr A pointer to the sResource data structure.
<symbol\>\xab spParamData LongInt On input: fAll, fOneslot flags.
The SGetTypeSRsrc function allows you to find the next sResource data structure of a certain type, as does the SNextTypeSRsrc function, but the SGetTypeSRsrc function also allows you to find disabled sResource data structures and to limit searching to a single slot.
You specify an sResource data structure with the spSlot, spID, and spExtDev fields, and you specify the type of the sResource with the spCategory, spCType, spDrvrSW, and spDrvrHW fields. You must also use the spTBMask to specify which of these sRsrcType fields should not be included in the search:
n Set bit 0 to ignore the DrvrHW field.
n Set bit 1 to ignore the DrvrSW field.
n Set bit 2 to ignore the cType field.
n Set bit 3 to ignore the Category field.
You must also set the fAll flag of the spParamData field (bit 0) to search both enabled and disabled sResource data structures or clear this flag to search only enabled ones. Set the fOneslot flag (bit 1) to search only the specified slot or clear this flag to search all slots. This function does not use the fNext flag (bit 2) because it always searches for the next sResource data structure of the given type.
The SGetTypeSRsrc function returns values in the spSlot, spID, and spExtDev fields corresponding to the sResource data structure that it found, and it returns 0 in the spParamData field in that sResource is enabled or 1 if it is disabled.
The SGetTypeSRsrc function also returns a pointer to the sResource data structure in the spsPointer field and other information about the sResource data structure in the spRefNum, spCategory, spCType, spDrvrSW, spDrvrHW, and spHwDev fields.
SPECIAL CONSIDERATIONS
The SGetTypeSRsrc function does not move memory; your application may call this function at interrupt time.
The SGetTypeSRsrc function is available only with version 1 or later of the Slot Manager.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SGetTypeSRsrc function is _SlotManager ($A06E). The routine selector is $000C.
Trap macro Selector
_SlotManager $000C
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $000C
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource data structure, see the section “Firmware” on page 3-7.
For information on enabling and disabling sResource data structures, see the “Enabling and Disabling sResource Data Structures” on page 3-15 and the description of the SetSRsrcState function on page 3-35.
Enabling, Disabling, Deleting, and Restoring sResource
Data Structures
The routines in this section are primarily for use by device drivers. The first routine enables and disables sResource data structures. The next two routines delete sResource data structures from and restore them to the Slot Resource Table.
3SetSRsrcState
You can use the SetSRsrcState function to select which sResource data structures are enabled.
FUNCTION SetSRsrcState (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spParamData LongInt Enable or disable the resource.
<symbol\>\xae spSlot SignedByte The slot number.
<symbol\>\xae spId SignedByte The sResource ID.
<symbol\>\xae spExtDev SignedByte The external device identifier.
DESCRIPTION
The SetSRsrcState function enables or disables an sResource data structure. All of the Slot Manager routines recognize enabled sResource data structures, while only the SGetSRsrc and SGetTypeSRsrc functions can recognize disabled ones.
You specify the sResource data structure to enable or disable with the spSlot, spID, and spExtDev fields, and in the spParamData field you specify whether to enable or disable it. The Slot Manager enables the sResource data structure when this field has a value of 0 and disables it when the field has a value of 1.
SPECIAL CONSIDERATIONS
The SetSRsrcState function does not move memory; your application may call this function at interrupt time.
The SetSRsrcState function is available only with version 1 or later of the Slot Manager.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SetSRsrcState function is _SlotManager ($A06E). The routine selector is $0009.
Trap macro Selector
_SlotManager $0009
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0009
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource data structure, see the section “Firmware” on page 3-7.
For information on enabling and disabling sResource data structures, see the “Enabling and Disabling sResource Data Structures” on page 3-15 and the description of the SetSRsrcState function on page 3-35.
For information on finding disabled sResource data structures see the description of the SGetSRsrc function on “SGetSRsrc” on page 3-30 and the description of the SGetTypeSRsrc function on “SGetTypeSRsrc” on page 3-33.
3SDeleteSRTRec
You can use the SDeleteSRTRec function to remove an sResource data structure from the Slot Resource Table.
FUNCTION SDeleteSRTRec (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spSlot SignedByte The slot number.
<symbol\>\xae spId SignedByte The sResource ID.
<symbol\>\xae spExtDev SignedByte The external device identifier.
SPECIAL CONSIDERATIONS
The SDeleteSRTRec function may move memory; your application should not call this function at interrupt time.
The SDeleteSRTRec function is available only with version 1 or later of the Slot Manager.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SDeleteSRTRec function is _SlotManager ($A06E). The routine selector is $0031.
Trap macro Selector
_SlotManager $0031
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0031
Registers on exit
D0 result code
SEE ALSO
For more information about the Slot Resource Table, see “About the Slot Manager” on page 3-12.
For more information about restoring an sResource data structure to the Slot Resource Table, see the next section, which describes the InsertSRTRec function.
3InsertSRTRec
You can use the InsertSRTRec function to add an sResource data structure to the Slot Resource Table.
FUNCTION InsertSRTRec (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spsPointer Ptr NIL.
<symbol\>\xae spParamData LongInt Enable or disable the sResource.
The InsertSRTRec function installs an sResource data structure from the firmware of a NuBus card into the Slot Resource Table. For example, if the user makes a selection in the Monitors control panel that requires your video card to switch to a new sResource data structure that was deleted by PrimaryInit code, you can use the InsertSRTRec function to restore that sResource data structure.
You specify an sResource data structure with the spSlot, spID, and spExtDev fields. You must set the spsPointer field to NIL. Set the spParamData field to 1 to disable the restored sResource data structure or to 0 to enable it.
If you place a valid device driver reference number in the spRefNum field, the Slot Manager updates the dCtlDevBase field in that device driver’s device control entry (that is, in the device control entry that has that driver reference number in the dCtlRefNum field). The dCtlDevBase field contains the base address of the memory buffer for data provided by the sResource data structure that is used by that device driver.
The Slot Manager calculates this address by using bit 2 (the f32BitMode flag) of the sRsrcFlags field of the sResource data structure. As shown in Table 3-4, the Slot Manager first checks the value of bit 2 of the sRsrcFlags. Then, it checks for a MinorBaseOS entry. If it finds one, it uses this value to create a 32-bit value to store in the dCtlDevBase field. If it does not find one, it uses the value in the MajorBaseOS entry.
How the Slot Manager determines the base address used by an sResource
sRsrcFlags MinorBaseOS MajorBaseOS Address format
Field missing $x xxxx Any or none $Fs0x xxxx
Field missing None $xx xxxx $sxxx xxxx
Bit 2 is 0 $x xxxx Any or none $Fs0x xxxx
Bit 2 is 0 None $xx xxxx $sxxx xxxx
Bit 2 is 1 $x xxxx Any or none $Fsxx xxxx
Bit 2 is 1 None $xx xxxx $sxxx xxxx
In this table, x represents any hexadecimal digit and s represents a slot number. <36pt\>\x12 <8bat\>u
SPECIAL CONSIDERATIONS
The InsertSRTRec function may move memory; your application should not call this function at interrupt time.
The InsertSRTRec function is available only with version 1 or later of the Slot Manager.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the InsertSRTRec function is _SlotManager ($A06E). The routine selector is $000A.
Trap macro Selector
_SlotManager $000A
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
smNoMoresRsrcs –344 Specified sResource data structure not found
smBadsPtrErr –346 Bad pointer was passes to SCalcSPointer
smByteLanesErr –347 ByteLanes field in card’s format block was
determined to be zero
SEE ALSO
For more information about the sResource data structure, see the section “Firmware” on page 3-7.
For more information about the device control entry and device driver reference numbers, see the chapter “The Device Manager” in this volume.
Getting Information from sResource Data Structures
The Slot Manager provides a number of routines that simplify access to the information in sResource data structures. Most of these routines simply return the value of an sResource list entry.
3SReadDrvrName
You can use the SReadDrvrName function to read the name of an sResource data structure in a format you can use to open the driver with Device Manager routines.
FUNCTION SReadDrvrName (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spSlot SignedByte The slot number.
<symbol\>\xae spID SignedByte The sResource ID.
<symbol\>\xae spResult LongInt Pointer to driver name.
5 spSize LongInt
5 spsPointer Ptr
DESCRIPTION
The SReadDrvrName function reads the name of an sResource data structure, prefixes a period to the value, and converts it to type Str255. The final driver name is compatible with the Device Manager’s OpenDriver function.
You specify the sResource data structure with the spSlot and spID fields. In your program, you should declare a Pascal string variable and pass a pointer to it in the spResult field.
The SReadDrvrName function returns the driver name by copying it into the string pointed to by the spResult field.
This function may alter the values of the spSize and spsPointer fields of the parameter block. Your application should not depend on the values returned in these fields.
SPECIAL CONSIDERATIONS
The SReadDrvrName function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadDrvrName function is _SlotManager ($A06E). The routine selector is $0019.
Trap macro Selector
_SlotManager $0019
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0019
Registers on exit
D0 result code
RESULT CODES
noErr 0 No Error
smNoMoresRsrcs –344 Requested sResource data structure not found
SEE ALSO
For more information about the sResource data structure, see the section “Firmware” on page 3-7.
For more information about the device control entry and device driver reference numbers, see the chapter “The Device Manager” in this volume.
3SOffsetData
You can use the SOffsetData function to determine the value of the offset field of an sResource list entry.
FUNCTION SOffsetData (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xab spsPointer Ptr On input: Pointer to the sResource list.
On output: Pointer to sResource list entry.
<symbol\>\xac spOffsetData LongInt The contents of the offset field.
<symbol\>\xae spID SignedByte The ID of the sResource list entry.
<symbol\>\xac spByteLanes SignedByte Byte lanes from card format block.
5 spResult LongInt
5 spFlags SignedByte
DESCRIPTION
The SOffsetData function returns the contents of an offset field of an entry in an sResource list. This function provides low-level support for determining information in sResource list data structures. Many of the remaining routines in this section provide higher-level support.
You provide a pointer to the sResource list in the spsPointer field, and the ID of the entry in the spID field.
The SOffsetData function returns the value of the offset field, which can be a byte or a word of data or an offset to a larger block of data, in the spOffsetData field of the parameter block. The return value of the spsPointer field is a pointer to the specified entry in the sResource list—that is, an pointer to the entry’s ID field.
This function uses the spByteLanes field to return the value of the ByteLanes field from the format block of the card on which the sResource data structure resides.
Your application should not depend on the values returned in the spResult and spFlags fields.
SPECIAL CONSIDERATIONS
The SOffsetData function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SOffsetData function is _SlotManager ($A06E). The routine selector is $0024.
Trap macro Selector
_SlotManager $0024
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0024
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource list data structure, see the “Introduction to Cards and Slots” section at the beginning of this chapter.
For information about routines that provide higher-level support for getting information from sResource lists, see the following reference sections which describe the SReadByte, SReadWord, SReadLong, SGetCString, SGetBlock, SReadStruct, and SFindStruct routines.
3SReadByte
You can use the SReadByte function to determine the value of the low-order byte of an sResource list entry.
FUNCTION SReadByte (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
You specify values and receive return values in the Slot Manager parameter block.
<symbol\>\xac spResult LongInt Contents of the entry byte.
<symbol\>\xae spsPointer Ptr Pointer to the sResource list.
<symbol\>\xae spID SignedByte The ID of the sResource list entry.
5 spOffsetData LongInt
5 spByteLanes SignedByte
DESCRIPTION
You provide a pointer to the sResource list in the spsPointer field, and the ID of the entry in the spID field. The SReadByte function returns the data byte in the spResult field.
This function may alter the values of the spOffsetData and spByteLanes fields of the parameter block. Your application should not depend on the values returned in these fields.
SPECIAL CONSIDERATIONS
The SReadByte function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadByte function is _SlotManager ($A06E). The routine selector is $0000.
Trap macro Selector
_SlotManager $0000
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0000
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource list data structure, see the section “Firmware” on page 3-7.
3SReadWord
You can use the SReadWord function to determine the value of the low-order word of an sResource list entry.
FUNCTION SReadWord (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spResult LongInt Contents of the entry word.
<symbol\>\xae spsPointer Ptr Pointer to the sResource list.
<symbol\>\xae spID SignedByte The ID of the sResource list entry.
5 spOffsetData LongInt
5 spByteLanes SignedByte
SPECIAL CONSIDERATIONS
The SReadWord function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadWord function is _SlotManager ($A06E). The routine selector is $0001.
Trap macro Selector
_SlotManager $0001
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0001
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource list data structure, see the section “Firmware” on page 3-7.
3SReadLong
You can use the SReadLong function to determine the value of a longword pointed to by the offset field of an sResource list entry.
FUNCTION SReadLong (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spResult LongInt Contents of the longword.
<symbol\>\xae spsPointer Ptr Pointer to the sResource list.
<symbol\>\xae spID SignedByte The ID of the sResource list entry.
5 spSize LongInt
5 spOffsetData LongInt
5 spByteLanes SignedByte
DESCRIPTION
The SReadLong function returns the 32-bit value pointed to by the offset field of an sResource list entry.
You provide a pointer to the sResource list in the spsPointer field, and the ID of the entry in the spID field. The SReadLong function returns the longword value in the spResult field.
This function may alter the values of the spSize, spOffsetData, and spByteLanes fields of the parameter block. Your application should not depend on the values returned in these fields.
SPECIAL CONSIDERATIONS
The SReadLong function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadLong function is _SlotManager ($A06E). The routine selector is $0002.
Trap macro Selector
_SlotManager $0002
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0002
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource list data structure, see the section “Firmware” on page 3-7.
3SGetCString
You can use the SGetCString function to determine the value of a string pointed to by the offset field of an sResource list entry.
FUNCTION SGetCString (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spResult LongInt Pointer to copy of cString.
<symbol\>\xae spsPointer Ptr Pointer to the sResource list.
<symbol\>\xae spID SignedByte The ID of the sResource list entry.
5 spSize LongInt
5 spOffsetData LongInt
5 spByteLanes SignedByte
5 spFlags SignedByte
DESCRIPTION
The SGetCString function returns a copy of the cString data structure pointed to by the offset field of an sResource list entry.
You provide a pointer to the sResource list in the spsPointer field, and the ID of the entry in the spID field.
The SGetCString function allocates a memory buffer, copies the value of the cString data structure into it, and returns a pointer to it in the spResult field. You should dispose of this pointer using the Memory Manager routine DisposPtr.
This function may alter the values of the spSize, spOffsetData, spByteLanes, and spFlags fields of the parameter block. Your application should not depend on the values returned in these fields.
SPECIAL CONSIDERATIONS
The SGetCString function allocates memory; your application should not call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SGetCString function is _SlotManager ($A06E). The routine selector is $0003.
Trap macro Selector
_SlotManager $0003
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0003
Registers on exit
D0 result code
SEE ALSO
For more information about the cString data structure, see the section “Firmware” on page 3-7.
3SGetBlock
You can use the SGetBlock function to obtain a copy of an sBlock data structure pointed to by the offset field of an sResource list entry.
FUNCTION SGetBlock (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spResult LongInt Pointer to copy of sBlock.
<symbol\>\xae spsPointer Ptr Pointer to the sResource list.
<symbol\>\xae spID SignedByte The ID of the sResource list entry.
5 spSize LongInt
5 spOffsetData LongInt
5 spByteLanes SignedByte
5 spFlags SignedByte
DESCRIPTION
The SGetBlock function returns a copy of the sBlock data structure pointed to by the offset field of an sResource list entry.
You provide a pointer to the sResource list in the spsPointer field, and the ID of the entry in the spID field.
The SGetBlock function allocates a memory buffer, copies the contents of the sBlock data structure into it, and returns a pointer to it in the spResult field. You should dispose of this pointer using the Memory Manager routine DisposPtr.
This function may alter the values of the spSize, spOffsetData, spByteLanes, and spFlags fields of the parameter block. Your application should not depend on the values returned in these fields.
SPECIAL CONSIDERATIONS
The SGetBlock function allocates memory; your application should not call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SGetBlock function is _SlotManager ($A06E). The routine selector is $0005.
Trap macro Selector
_SlotManager $0005
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0005
Registers on exit
D0 result code
SEE ALSO
For more information about the sBlock data structure, see the section “Firmware” on page 3-7.
For information on how to determine the size of an sBlock data structure, see the next section, which describes the SReadPBSize function.
3SReadPBSize
You can use the SReadPBSize function to determine the size of an sBlock data structure.
FUNCTION SReadPBSize (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xab spsPointer Ptr Pointer to sResource list.
<symbol\>\xac spSize LongInt Size of the sBlock data structure.
<symbol\>\xab spsPointer Ptr Pointer to sResource lists.
<symbol\>\xae spID SignedByte ID of the sBlock in the sResource list.
<symbol\>\xac spByteLanes SignedByte Byte lanes from format block.
You provide a pointer to the sResource list in the spsPointer field and the ID of the sBlock entry in the spID field. You can also specify the fckReserved flag of the spFlags field.
The SReadPBSize function returns the physical size of the block in the spSize field and a pointer to the block in the spsPointer field.
This function uses the spByteLanes field to return the value of the ByteLanes field from the format block of the card on which the sResource data structure resides.
SPECIAL CONSIDERATIONS
The SReadPBSize function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadPBSize function is _SlotManager ($A06E). The routine selector is $0026.
Trap macro Selector
_SlotManager $0026
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $00026
Registers on exit
D0 result code
SEE ALSO
For more information about the sBlock data structure, see the section “Firmware” on page 3-7.
3SFindStruct
The SFindStruct function allows you to obtain a pointer to any data structure pointed to by the offset field of an sResource list entry. You might want to use this function, for example, when the data structure type is defined by the card designer.
FUNCTION SFindStruct (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xab spsPointer Ptr On input: pointer to the sResource list.
On output: pointer to data structure.
<symbol\>\xae spID SignedByte The ID of the sResource list entry.
5 spByteLanes SignedByte
DESCRIPTION
You provide a pointer to the sResource list in the spsPointer field, and the ID of the entry in the spID field.
The SFindStruct function returns a pointer to the data structure in the spResult field.
This function may alter the value of the spByteLanes field of the parameter block. Your application should not depend on the value returned in this field.
SPECIAL CONSIDERATIONS
The SFindStruct function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SFindStruct function is _SlotManager ($A06E). The routine selector is $0006.
Trap macro Selector
_SlotManager $0006
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0006
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource list data structure, see the section “Firmware” on page 3-7.
For information about obtaining a copy of a data structure pointed to by the offset field of an sResource list entry, rather than a pointer to the data structure, see the next section, which describes the SReadStruct function.
3SReadStruct
The SReadStruct function allows you obtain a copy of any data structure offset from an sResource list entry. You might want to use this function, for example, when the data structure type is defined by the card designer.
FUNCTION SReadStruct (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
You specify values and receive return values in the Slot Manager parameter block.
<symbol\>\xae spResult LongInt Pointer to memory block.
<symbol\>\xae spsPointer Ptr Pointer to the structure.
<symbol\>\xae spSize LongInt The length in bytes of the structure.
5 spByteLanes SignedByte
DESCRIPTION
The SReadStruct function copies any arbitrary data structure from the declaration ROM of an expansion card into memory.
You provide a pointer to the structure in the spsPointer field and the size of the structure in the spSize field. You must also allocate a memory block for the result and send a pointer to it in the spResult field.
The SReadStruct function copies the data structure into the memory block pointed to by the spResult field.
This function may alter the value of the spByteLanes field of the parameter block. Your application should not depend on the value returned in this field.
SPECIAL CONSIDERATIONS
The SReadStruct function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadStruct function is _SlotManager ($A06E). The routine selector is $0007.
Trap macro Selector
_SlotManager $0007
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0007
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource list data structure, see the section “Firmware” on page 3-7.
For information about obtaining a pointer to a data structure pointed to by the offset field of an sResource list entry, rather than a copy of the data structure, see the previous section, which describes the SFindStruct function.
Loading Drivers and Executing Code from sResource
Data Structures
The routines in this section allow you to load the device driver associated with an sResource data structure or execute code from an SExecBlock data structure.
Both of the routines in this section require you to provide extra information in an SEBlock, or SExec parameter block, data structure, shown in Listing 3-5.
SEBlock (sExec parameter block) data structure
SEBlockPtr = ^SEBlock;
SEBlock = PACKED RECORD
seSlot: SignedByte; {Slot number}
sesRsrcID: SignedByte; {sResource ID}
seStatus: INTEGER; {Status of sExecBlock code}
seFlags: SignedByte; {Flags}
seFiller0: SignedByte; {Used for word alignment}
seFiller1: SignedByte; {Used for word alignment}
seFiller2: SignedByte; {Used for word alignment}
seResult: LONGINT; {Result of SLoad}
seIOFileName: LONGINT; {Pointer to IOFile name}
seDevice: SignedByte; {Which device to read from}
sePartition: SignedByte; {The partition}
seOSType: SignedByte; {Type of OS}
seReserved: SignedByte; {Reserved}
seRefNum: SignedByte; {Driver reference number}
seNumDevices: SignedByte; {Number of devices to load}
seBootState: SignedByte; {State of StartBoot code}
END;
3SGetDriver
You can use the SGetDriver function to load an sResource data structure’s device driver.
FUNCTION SGetDriver (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spResult LongInt Pointer to memory block.
<symbol\>\xae spsExecPBlk Ptr Pointer to SEblock.
<symbol\>\xae spSlot SignedByte The slot number.
<symbol\>\xae spID SignedByte The sResource ID.
<symbol\>\xae spExtDev SignedByte The external device ID.
5 spSize SignedByte
5 spFlags SignedByte
DESCRIPTION
The SGetDriver function loads a device driver from an sResource data structure into memory.
You specify an sResource data structure with the spSlot, spID, and D fields, and provide a pointer to an SExecBlock in the spsExecPBlk field.
The SGetDriver function searches the sResource list for an sRsrcLoadRec entry. If it finds one, it loads the sLoadDriver record and executes it. If no sRsrcLoadRec entry exists, the SGetDriver function looks for an sRsrcDrvrDir entry. If it finds one, it loads the driver into memory.
The SGetDriver function returns a handle to the driver in the spResult field of the parameter block.
SPECIAL CONSIDERATIONS
The SGetDriver function may move memory; your application should not call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SGetDriver function is _SlotManager ($A06E). The routine selector is $002D.
Trap macro Selector
_SlotManager $002D
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register A0 contains a handle to the loaded driver and register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $002D
Registers on exit
A0 handle to loaded driver
D0 result code
SEE ALSO
For more information about the sResource data structure, including the sRsrcDrvrDir and sRsrcLoadRec entry types, see Designing Cards and Drivers for the Macintosh Family.
3SExec
You can use the SExec function to execute code stored in an sExecBlock data structure.
FUNCTION SExec (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spsPointer Ptr Pointer to sResource list.
<symbol\>\xae spsExecPBlk Ptr Pointer to SEBlock data structure.
<symbol\>\xae spID SignedByte ID of sExecBlock entry in sResource list.
5 spResult LongInt
DESCRIPTION
The SExec function loads SExecBlock code from an sResource list into the current heap zone, checks its revision level and CRC field, and executes the code.
You specify the SExecBlock by providing a pointer to the sResource list in the spsPointer field and the ID of the SExecBlock entry in the spID field. You must also provide in the spsExecPBlk field a pointer to an SExec parameter block. This data structure, shown in Listing 3-5, allows you to provide information about the execution of the SExecBlock code.
The SExec function passes the sExecBlock code a pointer to this data structure in register A0.
SPECIAL CONSIDERATIONS
The SExec function may move memory; your application should not call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SExec function is _SlotManager ($A06E). The routine selector is $0023.
Trap macro Selector
_SlotManager $0023
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0023
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource data structure and the sExecBlock data structure, see the section “Firmware” on page 3-7.
For more information about device drivers, see the chapter “The Device Manager” of this volume.
Traversing Firmware of Expansion Cards
Because declaration ROMs may not use all four byte lanes, the difference between the addresses of two adjacent blocks may be greater than the size in bytes of the first block. The SCalcSPointer function can help you calculate the address of the next block in a declaration ROM, given a pointer to the current block and its size in bytes.
3SCalcSPointer
You can use the SCalcSPointer function to obtain a pointer to a given byte in a card’s declaration ROM.
FUNCTION SCalcSPointer (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xab spsPointer Ptr Pointer to a byte in declaration ROM.
<symbol\>\xae spOffsetData LongInt Offset in bytes to desired pointer.
You specify the byte by providing a pointer to the current byte in the spsPointer field, an offset in bytes in the spOffset data field, and the valid byte lanes in the spByteLanes field.
The SCalcSPointer function returns the calculated pointer in the spsPointer field.
SPECIAL CONSIDERATIONS
The SCalcSPointer function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SCalcSPointer function is _SlotManager ($A06E). The routine selector is $002C.
Trap macro Selector
_SlotManager $002C
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $002C
Registers on exit
D0 result code
SEE ALSO
For more information about the sResource list data structure, see the section “Firmware” on page 3-7.
Getting Information about Expansion Cards and
Declaration ROMs
The functions in this section return information about slot status, or information about entire declaration ROMs, instead of single sResource data structures.
The first two functions return information from the slot information record maintained by the Slot Manager for a particular slot. Listing 3-5 shows the definition of this data type.
SEBlock (SExec parameter block) data structure
TYPE SInfoRecPtr = ^SInfoRecord;
SInfoRecord = PACKED RECORD
siDirPtr: Ptr; {pointer to sResource directory}
siInitStatusA: INTEGER; {initialization error}
siInitStatusV: INTEGER; {status returned by vendor}
{ initialization routine}
siState: SignedByte; {initialization state}
siCPUByteLanes: SignedByte; {byte lanes used}
siTopOfROM: SignedByte; {highest valid address in ROM}
siStatusFlags: SignedByte ; {bit 0 is CardIsChanged flag}
siTOConstant: INTEGER; {Timeout constant for bus error}
siReserved: SignedByte; {reserved}
END;
3SFindSInfoRecPtr
You can use the SFindSInfoRecPtr function to obtain a pointer to the SInfoRecord data structure for a particular slot.
FUNCTION SFindSInfoRecPtr (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xac spResult LongInt Pointer to the SInfoRecord data structure.
<symbol\>\xae spSlot SignedByte The slot number.
DESCRIPTION
You specify the slot with the spSlot parameter. You must also allocate an sInfoRecord data structure, and provide a pointer to it in the spResult field.
The SFindSInfoRecPtr function returns in the spResult field a pointer to the sInfoRecord data structure for the specified slot.
SPECIAL CONSIDERATIONS
The SFindSInfoRecPtr function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SFindSInfoRecPtr function is _SlotManager ($A06E). The routine selector is $002F.
Trap macro Selector
_SlotManager $002F
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $002F
Registers on exit
D0 result code
SEE ALSO
For general information about the SInfoRecord data structure, see “About the Slot Manager” on page 3-12.
To obtain a copy of the SInfoRecord data structure, instead of a pointer to it, see the next section, which describes the SReadInfo function.
3SReadInfo
You can use the SReadInfo function to obtain a copy of the SInfoRecord data structure for a particular slot.
FUNCTION SReadInfo (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spResult LongInt Pointer to an SInfoRecord data structure.
<symbol\>\xae spSlot SignedByte The slot number.
<zapf\>5 spSize LongInt
DESCRIPTION
The Slot Manager maintains an SInfoRecord data structure for each slot. The SReadInfo function copies the information from this data structure for the requested slot.
You specify the slot with the spSlot parameter. You must also allocate an SInfoRecord data structure, and provide a pointer to it in the spResult field. The SReadInfo function copies the information in the sInfo record maintained by the Slot Manager into the data structure pointed to by the spResult field.
This function may alter the contents of the spSize field. Your application should not depend on the value returned in this field.
SPECIAL CONSIDERATIONS
The SReadInfo function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadInfo function is _SlotManager ($A06E). The routine selector is $0010.
Trap macro Selector
_SlotManager $0010
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0010
Registers on exit
D0 result code
SEE ALSO
For general information about sInfoRecord data structures, see the “About the Slot Manager” section.
To obtain a pointer of the SInfoRecord data structure, instead of a copy of it, see the next section, which describes the SReadInfo function.
3SReadFHeader
You can use the SReadFHeader function to obtain a copy of the information in the format block of a declaration ROM.
FUNCTION SReadFHeader (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spResult LongInt Pointer to an FHeaderRec data structure.
<symbol\>\xae spSlot SignedByte Slot number.
<zapf\>5 spsPointer Ptr
<zapf\>5 spSize LongInt
<zapf\>5 spOffsetData LongInt
<zapf\>5 spByteLanes SignedByte
DESCRIPTION
The SReadFHeader function copies the information from the format block of the expansion card in the requested slot to an FHeaderRec data structure you provide.
You specify the slot with the spSlot parameter. You must also allocate an FHeaderRec data structure, defined in Listing 3-7, and provide a pointer to it in the spResult field.
The SReadInfo function copies the information in the format block into the data structure pointed to by the spResult field.
This function may alter the contents of the spsPointer, spSize, spOffsetData, and spByteLanes fields. Your application should not depend on the values returned in these fields.
The FHeaderRec data structure
TYPE FHeaderRecPtr = ^FHeaderRec;
FHeaderRec = PACKED RECORD
fhDirOffset: LongInt; {offset to sResource directory}
fhLength: LongInt; {length in bytes of declaration ROM}
fhCRC: LongInt; {cyclic redundancy check}
fhROMRev: SignedByte; {declaration ROM revision}
fhFormat: SignedByte; {declaration ROM format}
fhTstPat: LongInt; {test pattern}
fhReserved: INTEGER; {reserved, should be $00}
fhByteLanes: SignedByte; {byte lanes used by declaration ROM}
END;
These fields correspond to fields of the format header, described in Designing Cards and Drivers for the Macintosh Family.
SPECIAL CONSIDERATIONS
The SReadFHeader function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadFHeader function is _SlotManager ($A06E). The routine selector is $0013.
Trap macro Selector
_SlotManager $0013
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0013
Registers on exit
D0 result code
SEE ALSO
For general information about the format block, see the section “Firmware” on page 3-7.
For information about the fields of the format block, see Designing Cards and Drivers for the Macintosh Family.
3SCkCardStat
You can use the SCkCardStat function to check the initialization status of an expansion card.
FUNCTION SCkCardStat (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spSlot SignedByte The slot number.
<zapf\>5 spResult LongInt
DESCRIPTION
The SCkCardStat function checks the InitStatusA field of the SInfoRecord data structure for the expansion card in the designated slot.
You specify the slot with the spSlot parameter.
The SCkCardStat function returns the noErr constant if the InitStatusA field contains a nonzero value.
This function may alter the contents of the spResult field. Your application should not depend on the values returned in this field.
SPECIAL CONSIDERATIONS
The SCkCardStat function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SCkCardStat function is _SlotManager ($A06E). The routine selector is $0018.
Trap macro Selector
_SlotManager $0018
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0018
Registers on exit
D0 result code
SEE ALSO
For more information about the SInfoRecord data type, see “Getting Information about Expansion Cards and Declaration ROMs” on page 3-58.
3SCardChanged
You can use the SCardChanged function to determine if the card in a particular slot has been changed.
FUNCTION SCardChanged (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
The SCardChanged function checks if the expansion card in a slot has been changed—that is, if its sPRAMInit record has been initialized.
You specify the slot with the spSlot parameter.
The SCardChanged function returns a value of TRUE in the spResult field of the parameter block if the card has changed.
SPECIAL CONSIDERATIONS
The SCardChanged function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SCardChanged function is _SlotManager ($A06E). The routine selector is $0022.
Trap macro Selector
_SlotManager $0022
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0022
Registers on exit
D0 result code
SEE ALSO
For more information about the SPRAMInit record, see the next section, “Getting Access to Expansion Cards’ Parameter RAM.”
Getting Access to Expansion Cards’ Parameter RAM
The Operating System reserves six bytes of parameter RAM for each slot. The routines in this section allow you to read or change the value of these bytes.
Both of the functions in this section use the SPRAMRecord data structure to contain the parameter RAM values.
The SPRAM Record data type
TYPE SPRAMRecPtr = ^SPRAMRecord;
SPRAMRecord = PACKED RECORD
boardID: INTEGER; {Apple-defined card ID}
vendorUse1: SignedByte; {Reserved for vendor use}
vendorUse2: SignedByte; {Reserved for vendor use}
vendorUse3: SignedByte; {Reserved for vendor use}
vendorUse4: SignedByte; {Reserved for vendor use}
vendorUse5: SignedByte; {Reserved for vendor use}
vendorUse6: SignedByte; {Reserved for vendor use}
END;
This data structure includes the Apple-defined BoardID and six bytes of parameter RAM information.
3SReadPRAMRec
You can use the SReadPRAMRec function to read the parameter RAM information for a particular slot.
FUNCTION SReadPRAMRec (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spSlot SignedByte The slot number.
<symbol\>\xae spResult LongInt Pointer to SPRAMRecord data structure.
<zapf\>5 spSize LongInt
DESCRIPTION
The Operating System allocates one SPRAMRecord data structure for each slot in the system parameter RAM. The Slot Manager routine InitSPRAMRecs initializes this structure with the data from the sPRAMInit record on the firmware of the expansion card. The SReadPRAMRec function provides a copy of this information to your application.
You specify the slot number in the spSlot field. You must also allocate a SPRAMRecord data structure and send a pointer to it in the spResult field. The SReadPRAMRec function copies the appropriate parameter RAM information into this data structure.
SPECIAL CONSIDERATIONS
The SReadPRAMRec function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SReadPRAMRec function is _SlotManager ($A06E). The routine selector is $0011.
Trap macro Selector
_SlotManager $0011
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0011
Registers on exit
D0 result code
SEE ALSO
For more information about the sPRAMInit record, see Designing Cards and Drivers for the Macintosh Family.
3SPutPRAMRec
You can use the SPutPRAMRec function to change the values stored in a slot’s parameter RAM.
FUNCTION SPutPRAMRec (spBlkPtr: SpBlockPtr) : OSErr;
spBlkPtr Pointer to a Slot Manager parameter block.
<symbol\>\xae spsPointer Ptr Pointer to SPRAMRecord data structure.
<symbol\>\xae spSlot SignedByte The slot number.
DESCRIPTION
The SPutPRAMRec function allows you to change the values stored in the parameter RAM of a slot.
You specify the slot number with the spSlot field and provide the new parameter RAM values in a SPRAMRecord data structure pointed to by the spsPointer field.
The SPutPRAMRec function copies the information from the six vendor-use fields into the parameter RAM for the slot. This function does not copy the boardID field, which is Apple-defined.
SPECIAL CONSIDERATIONS
The SPutPRAMRec function does not move memory; your application may call this function at interrupt time.
ASSEMBLY-LANGUAGE INFORMATION
The trap macro for the SPutPRAMRec function is _SlotManager ($A06E). The routine selector is $0012.
Trap macro Selector
_SlotManager $0012
You must setup register D0 with the routine selector and register A0 with the address of the Slot Manager parameter block. When _SlotManager returns, register D0 contains the result code.
Registers on entry
A0 address of the parameter block
D0 $0012
Registers on exit
D0 result code
SEE ALSO
For more information about the sPRAMInit record, see the “Introduction to Cards and Slots” section at the beginning of this chapter.
Summary of the Slot Manager
Data Types
TYPE SpBlockPtr = ^SpBlock;
SpBlock = PACKED RECORD
spResult: LongInt; {result}
spsPointer: Ptr; {structure pointer}
spSize: LongInt; {size of structure}
spOffsetData: LongInt; {offset or data}
spIOFileName: Ptr; {reserved for Slot Manager}
spsExecPBlk: LongInt; {pointer to SEBlock data structure}
spParamData: Ptr; {flags}
spMisc: LongInt; {reserved for Slot Manager}
spReserved: LongInt; {reserved for Slot Manager}
spIOReserved: INTEGER; {ioReserved field from SRT}
spRefNum: INTEGER; {driver reference number}
spCategory: INTEGER; {Category field of sResource type}
spCType: INTEGER; {cType field of sResource type}
spDrvrSW: INTEGER; {DrvrSW field of sResource type}
spDrvrHW: INTEGER; {DrvrHW field of sResource type}
spTBMask: SignedByte; {sResource type bit mask}
spSlot: SignedByte; {slot number}
spID: SignedByte; {sResource ID}
spExtDev: SignedByte; {external device ID}
spHwDev: SignedByte; {hardware device ID}
spByteLanes: SignedByte; {valid byte lanes}
spFlags: SignedByte; {flags used by Slot Manager}
spKey: SignedByte; {reserved for Slot Manager}
END;
SInfoRecPtr = ^SInfoRecord;
SInfoRecord = PACKED RECORD
siDirPtr: Ptr; {pointer to sResource directory}
siInitStatusA: INTEGER; {initialization error}
siInitStatusV: INTEGER; {status returned by vendor}
{ initialization routine}
siState: SignedByte; {initialization state}
siCPUByteLanes: SignedByte; {byte lanes used}
siTopOfROM: SignedByte; {highest valid address in ROM}
siStatusFlags: SignedByte ; {bit 0 is CardIsChanged flag}
siTOConstant: INTEGER; {Timeout constant for bus error}
siReserved: SignedByte; {reserved}
END;
FHeaderRecPtr = ^FHeaderRec;
FHeaderRec = PACKED RECORD
fhDirOffset: LongInt; {offset to sResource directory}
fhLength: LongInt; {length in bytes of declaration ROM}
fhCRC: LongInt; {cyclic redundancy check}
fhROMRev: SignedByte; {declaration ROM revision}
fhFormat: SignedByte; {declaration ROM format}
fhTstPat: LongInt; {test pattern}
fhReserved: INTEGER; {reserved, should be $00}
fhByteLanes: SignedByte; {byte lanes used by declaration ROM}
END;
SPRAMRecPtr = ^SPRAMRecord;
SPRAMRecord = PACKED RECORD
boardID: INTEGER; {Apple-defined card ID}
vendorUse1: SignedByte; {Reserved for vendor use}
vendorUse2: SignedByte; {Reserved for vendor use}
vendorUse3: SignedByte; {Reserved for vendor use}
vendorUse4: SignedByte; {Reserved for vendor use}
vendorUse5: SignedByte; {Reserved for vendor use}
vendorUse6: SignedByte; {Reserved for vendor use}
END;
Routines
Determining the Version of the Slot Manager
FUNCTION SVersion (spBlkPtr: SpBlockPtr) : OSErr;
Finding sResource Data Structures
FUNCTION SFindSRsrcPtr (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SGetSRsrcPtr (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SRsrcInfo (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SNextSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SGetSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SNextTypeSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SGetTypeSRsrc (spBlkPtr: SpBlockPtr) : OSErr;
Enabling, Disabling, and Restoring sResource Data Structures
FUNCTION SetSRsrcState (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SDeleteSRTRec (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION InsertSRTRec (spBlkPtr: SpBlockPtr) : OSErr;
Getting Information from sResource Data Structures
FUNCTION SReadDrvrName (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SOffsetData (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SReadByte (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SReadWord (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SReadLong (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SGetCString (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SGetBlock (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SReadPBSize (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SFindStruct (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SReadStruct (spBlkPtr: SpBlockPtr) : OSErr;
Loading Drivers and Executing Code from sResource Data Structures
FUNCTION SGetDriver (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SExec (spBlkPtr: SpBlockPtr) : OSErr;
Traversing Firmware of Expansion Cards
FUNCTION SCalcSPtr (spBlkPtr: SpBlockPtr) : OSErr;
Getting Information about Expansion Cards and Declaration ROMs
FUNCTION SFindSInfoRec (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SReadInfo (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SReadFHeader (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SCkCardStat (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SCardChanged (spBlkPtr: SpBlockPtr) : OSErr;
Getting Access to Expansion Cards’ Parameter RAM
FUNCTION SReadPRAMRec (spBlkPtr: SpBlockPtr) : OSErr;
FUNCTION SPutPRAMRec (spBlkPtr: SpBlockPtr) : OSErr;